From 128257c4bbf491688366c605dfe992db97cf9720 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 10:52:30 +0100 Subject: [PATCH 01/70] Create app.js --- apps/stressless/app.js | 303 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 apps/stressless/app.js diff --git a/apps/stressless/app.js b/apps/stressless/app.js new file mode 100644 index 000000000..b5e0ba8dd --- /dev/null +++ b/apps/stressless/app.js @@ -0,0 +1,303 @@ +var option = null; + +//debugging or analysis files +var logfile = require("Storage").open("HRV_log.csv", "w"); + +logfile = require("Storage").open("HRV_log.csv", "a"); + +var csv = [ + "time", + "sample count", + "HR", + "SDNN", + "RMSSD", + "Temp", + "movement" + ]; +logfile.write(csv.join(",")+"\n"); + +var debugging = true; +var samples = 0; // how many samples have we connected? +var collectData = false; // are we currently collecting data? + +var BPM_array = []; +var raw_HR_array = new Float32Array(1536); +var alternate_array = new Float32Array(3072); +var pulse_array = []; +var cutoff_threshold = 0.5; +var sample_frequency = 51.6; +var gap_threshold = 0.15; +var movement = 0; + +var px = g.getWidth()/2; +var py = g.getHeight()/2; +var accel; // interval for acceleration logging + +function storeMyData(data, file_type) { "ram" + 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) { + return E.sum(samples) / samples.length; // faster builtin + /* var sum = 0; + for (var i = 0; i < samples.length; i++) { + sum += parseFloat(samples[i]); + } + var avg = sum / samples.length; + return avg;*/ +} + +function StandardDeviation (array) { + const n = array.length; + const mean = E.sum(array) / n; //array.reduce((a, b) => a + b) / n; + //return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n); + return Math.sqrt(E.variance(array, mean)); +} + +function turn_off() { + Bangle.setHRMPower(0); + + + g.clear(); + g.drawString("processing 1/5", px, py); + + rolling_average(raw_HR_array,5); + g.clear(); + g.drawString("processing 2/5", px, py); + + upscale(); + g.clear(); + g.drawString("processing 3/5", px, py); + + rolling_average(alternate_array,5); + g.clear(); + g.drawString("processing 4/5", px, py); + + apply_cutoff(); + find_peaks(); + + g.clear(); + g.drawString("processing 5/5", px, py); + + calculate_HRV(); +} + +function bernstein(A, B, C, D, E, t) { "ram" + 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() { "ram" + 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) { "ram" + var 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() { "ram" + 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() { "ram" + 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 RMSSD(samples){ "ram" + var sum = 0; + var square = 0; + var data = []; + var value = 0; + + for (let i = 0; i < samples.length-1; i++) { + value = Math.abs(samples[i]-samples[i+1])*((1 / (sample_frequency * 2)) * 1000); + data.push(value); + } + + for (let i = 0; i < data.length; i++) { + square = data[i] * data[i]; + Math.round(square); + sum += square; + } + + var meansquare = sum/data.length; + var RMS = Math.sqrt(meansquare); + RMS = parseInt(RMS); + return RMS; +} + +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); + if(option == 0) + Bangle.setLCDPower(1); + g.clear(); + //var display_stdv = StandardDeviation(pulse_array).toFixed(1); + var SDNN = (StandardDeviation(temp_array) * (1 / (sample_frequency * 2) * 1000)).toFixed(0); + var RMS_SD = RMSSD(temp_array); + g.drawString("SDNN:" + SDNN + +"\nRMSSD:" + RMS_SD + + "\nHR:" + calculatedHR.toFixed(0) + +"\nSample Count:" + temp_array.length, px, py); + Bangle.setLCDPower(1); + if(option == 0) { // single run + Bangle.buzz(500,1); + option = null; + drawButtons(); + } else { + var csv = [ + 0|getTime(), + temp_array.length, + calculatedHR.toFixed(0), + SDNN, + RMS_SD, + E.getTemperature(), + movement.toFixed(5) + ]; + logfile.write(csv.join(",")+"\n"); + + + // for (let i = 0; i < raw_HR_array.length; i++) { + // raw_HR_array[i] = null; + //} + + turn_on(); + } +} + +function btn1Pressed() { + if(option === null){ + g.clear(); + g.drawString("one-off assessment", px, py); + option = 0; + + turn_on(); + } +} + +function btn3Pressed() { + if(option === null){ + logfile.write(""); //reset HRV log + g.clear(); + g.drawString("continuous mode", px, py); + option = 1; + + turn_on(); + } +} + +function turn_on() { + BPM_array = []; + pulse_array = []; + samples = 0; + if (accel) clearInterval(accel); + movement = 0; + accel = setInterval(function () { + movement = movement + Bangle.getAccel().diff; + }, 1000); + Bangle.setHRMPower(1); + collectData = true; +} + +function drawButtons() { + g.setColor("#00ff7f"); + g.setFont("6x8", 2); + g.setFontAlign(-1,1); + g.drawString("continuous", 120, 210); + g.drawString("one-time", 140, 50); + g.setColor("#ffffff"); + g.setFontAlign(0, 0); +} + +g.clear(); + +drawButtons(); + +g.setFont("6x8", 2); +g.setColor("#ffffff"); +g.setFontAlign(0, 0); // center font +g.drawString("check app README\nfor more info", px, py); + +setWatch(btn1Pressed, BTN1, {repeat:true}); +setWatch(btn3Pressed, BTN3, {repeat:true}); + + + +Bangle.on('HRM-raw', function (e) { + if (!collectData) return; + storeMyData(e.raw, 0); + if (!(samples & 7)) { + Bangle.setLCDPower(1); + g.clearRect(0, py-10, g.getWidth(), py+22); + if (samples < 100) + g.drawString("setting up...\nremain still " + samples + "%", px, py, true); + else + g.drawString("logging: " + (samples*100/raw_HR_array.length).toFixed(0) + "%", px, py, true); + } + if (samples > raw_HR_array.length) { + collectData = false; + turn_off(); + } + samples++; +}); From fc28c7306f9aeaf041bf92024d26afb832681084 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 10:59:05 +0100 Subject: [PATCH 02/70] add png --- apps/stressless/stressless.png | Bin 0 -> 1838 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/stressless/stressless.png diff --git a/apps/stressless/stressless.png b/apps/stressless/stressless.png new file mode 100644 index 0000000000000000000000000000000000000000..6f79d7c838d5807b01f379c196c9e280a645b099 GIT binary patch literal 1838 zcmV+}2hsS6P)j z3jVg|@-`{BCtZe8u#-~ow>-h0z{dC&quRsqe3U2HQfp5Nekx;&ds*AIZ81!UeSW~e zRU^Mq3ic~0Sjuxb^{2r%f^X|@VCx9aWi3(dNtbO>qW*KWffszb=Q3Gopkig+(v96) zXitvg->%i38W*2d6?iG)%~J55>Ichu&*f<;_`^ASE_>9r)of4j9_g|rkv$(7{FJZv zPr{AeD@FXifq?h&NK}l3&H1QdR@29m|IG4=;m_#6zhxR0LWJKZJSr5S=|w z@W8inZMd{K0G)dG&y;-g^|Ez@K*~zOtx$Z0GgKHECfSM?Ofu&9C&aO;*qL{!X_YjuL^NI_1RQ^x`daqCK56*xE|o#4*9& z;5nNX{>rCw2K_ZZ&XC&idZumC9h5%No&mmob+4VtcLtFwCU8D7f#B5E$VNE*i4tsK zVMpu1`~lUhNx640w{IQb4<8Ahd-m@O(k!f*aK`F6aG-ArJ+>%F9}6BI{XwPNf5@pM z!r)u^9_X4r`hz2BI98l1d*~cmyObWQ;W!_KR6jIgw+#Zr>j2XD^bVoZ9(CphRsby z1B7*uK7Jy%$dx3&_1D!;nS$%?M7*IY@Vz8rcS)BeLu0A8c9A!+p=gk>4suiO+Esnu z+x4PgMUM!+#~w+Yfru2bIpG;u&74s2d_ZSDBsHV>JN?qnssr!xv~uH30|MV02|hhP zol0p~2dO<}_CJYu6`p4)Pq6vUV2X6(W9=@m+@N6{_^X!HHp{rVwDG(I|Hhz#&yg4( z`&!}b)Ta(Q&h)!&sHlRS?-~~y<6KO!cc7lj4weFtziX{Q+qx3*o1LK$YHa+tC-}xv zcSW^%p1)={lg?0!H;&KE-YVCc@4mgpbcR!8qW)HULaHTYJW+qUb14Yb)qPC5Y(=DN z^Wfr_y-ee8`=oQJiAwPLz_Vouu%k6^3x+frBHo;Ty0iNkTlCiJ2C+#4-0uvv7=Yy( zaeX!aMv^XnQwqKl-Plcec3#$qt1lYRPz=CQ1JI@s_azZsjo6_P&-%W9mjQTR1MKyE zf0{;IXAn;qfH#Qf69(X^Y`?!C;wX*yJp=GBgZOvtdAElI$n$Q{0B;+_cB9lIMD!^Q zu-o=E0Go;EDucK!`yAMIsu6?uf${xjU#WYv*0*WIH5%Y0BKovJBn|M*Bw|7XbQ-0& zLHwHrn5z*_8N@~Qz1I3xjksSUuCio}QuQH$Kmm3q5yxr(lSG`T5ziTbF$Qs=@B3fL zwvW|_^GV9@y3SW>rUvM?psXelCmE&U261b)-)IZO0KB3R zPy4=qcSyh>witkeVPBH+vlggEe8&L%!$!bYYL*7rWg|}A-JgjJo`^n@O^Y8{V;b?n zY-+SxG9dwL)TUJc3Segd)}{o&G6V2W4KPn5{?*n_7TB^}1Z;#5Piw@V7{u)cpwR#v zv+1M(JOglteQ&csBc9hOzkmrrihz}%ve+m&@K}1(uBEIi`*$!?!WWQ5Njns(W zwV}@TyCK_ea?r1{^I8os$5(0w5q;DsRcBKx8)a^51QEZKeU53w1=bPv*@CuFvF%lX c|9_JI0mlPN+v_lqkpKVy07*qoM6N<$f>$k$_W%F@ literal 0 HcmV?d00001 From 5c17492486879d8e4bc933b7b69bdbfbf27cfb28 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:02:37 +0100 Subject: [PATCH 03/70] Create app-icon.js --- apps/stressless/app-icon.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/stressless/app-icon.js diff --git a/apps/stressless/app-icon.js b/apps/stressless/app-icon.js new file mode 100644 index 000000000..7eabcf2e9 --- /dev/null +++ b/apps/stressless/app-icon.js @@ -0,0 +1 @@ +E.toArrayBuffer(atob("mEwwhC/AH4AVhnM5nABJ4ACBYIADBJojFAAYnCBJIkJEwYJJFw/NEwYuGGAwJE6gSHGBAkFoYXNNZEzC60/7gXPA4nN/8sMB4GE4f/ngXqIYPf+f9AwPEC53NCYNPmfyVZoXD4fzRwMyAYKTMU4fT+fcn9NC4ZjJX4kv/8tGIXc4n/nr4DmgXGgf//5FDn8sSYPyAQIABHAQXEgE/+ZgB+g0BnnT+Xz6YXHCwRIC4X/SIPfnlPnk/MIRHEFwYXC5oPC4f9JIJoBph2IAASJE4fyPIKYCUo4wF74nB4XzLALICFxQwC4b0CRoL4DFxQwC5oMCn4UCbAouHMIbtLCxBhCAAMvRYwuKGAnfRYwuKGAh7CFyAwDPYYuPPQwWSPQhFQGBIWQDAoWSDAZFPh5iFCwP/Cpnw/4CC+ADBBAkPAYIdG+AmBGAPwAAItC/4JBBQI9FF4oRBF4oABDoYvGCAIXDAAIDBIYQLBBoQAxA")) From f6231de549559fd4375ca1088661b18e98439945 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:04:58 +0100 Subject: [PATCH 04/70] Create metadata.json --- apps/stressless/metadata.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 apps/stressless/metadata.json diff --git a/apps/stressless/metadata.json b/apps/stressless/metadata.json new file mode 100644 index 000000000..5d9786b4c --- /dev/null +++ b/apps/stressless/metadata.json @@ -0,0 +1,12 @@ +{ "id": "stressless", + "name": "Stressless", + "shortName":"Stressless", + "icon": "stressless.png", + "version":"0.01", + "description": "This is a heart activity tracker for PIIS stressless project", + "tags": "", + "storage": [ + {"name":"stressless.app.js","url":"app.js"}, + {"name":"stressless.img","url":"app-icon.js","evaluate":true} + ] +} From e0f9b5695aa51833235ca493d49e9f8c3fd62547 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:30:06 +0100 Subject: [PATCH 05/70] Update metadata.json --- apps/stressless/metadata.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/stressless/metadata.json b/apps/stressless/metadata.json index 5d9786b4c..8c27620d3 100644 --- a/apps/stressless/metadata.json +++ b/apps/stressless/metadata.json @@ -4,7 +4,8 @@ "icon": "stressless.png", "version":"0.01", "description": "This is a heart activity tracker for PIIS stressless project", - "tags": "", + "tags": "health", + "supports": ["BANGLEJS"], "storage": [ {"name":"stressless.app.js","url":"app.js"}, {"name":"stressless.img","url":"app-icon.js","evaluate":true} From 221138a98127a412b73e7486d65716d98e9079f6 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:34:34 +0100 Subject: [PATCH 06/70] Update app-icon.js --- apps/stressless/app-icon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stressless/app-icon.js b/apps/stressless/app-icon.js index 7eabcf2e9..0dd5ec32a 100644 --- a/apps/stressless/app-icon.js +++ b/apps/stressless/app-icon.js @@ -1 +1 @@ -E.toArrayBuffer(atob("mEwwhC/AH4AVhnM5nABJ4ACBYIADBJojFAAYnCBJIkJEwYJJFw/NEwYuGGAwJE6gSHGBAkFoYXNNZEzC60/7gXPA4nN/8sMB4GE4f/ngXqIYPf+f9AwPEC53NCYNPmfyVZoXD4fzRwMyAYKTMU4fT+fcn9NC4ZjJX4kv/8tGIXc4n/nr4DmgXGgf//5FDn8sSYPyAQIABHAQXEgE/+ZgB+g0BnnT+Xz6YXHCwRIC4X/SIPfnlPnk/MIRHEFwYXC5oPC4f9JIJoBph2IAASJE4fyPIKYCUo4wF74nB4XzLALICFxQwC4b0CRoL4DFxQwC5oMCn4UCbAouHMIbtLCxBhCAAMvRYwuKGAnfRYwuKGAh7CFyAwDPYYuPPQwWSPQhFQGBIWQDAoWSDAZFPh5iFCwP/Cpnw/4CC+ADBBAkPAYIdG+AmBGAPwAAItC/4JBBQI9FF4oRBF4oABDoYvGCAIXDAAIDBIYQLBBoQAxA")) +E.toArrayBuffer(atob("mEwxH+AH4A/AH4A/AFW7AAgThDBQeNCaYaSDg4TTFygcFCaYuWDgYTTFyHB5AmRGCARK4XJF7bXR5PQF9vQ6/CF8COL6/XYDgwERxYvBYEKOM67AhLQwEDFwTAGYywvIXIIhCAgPQSILAE4RmWF5AnBMAQECA4gJDMCovDGAfBQ4PR4K+C4RiCZAr3UFwgvDJ4IAB5IrEYAgJBHwaoF6HQNRIvFEQItDXYaTEYAQ+EAogADZZIvFGAYTBAgIBBFQg0CRwQREF5wuGeQpNDQYSUDAYYyFR5guIF4jyCdQ3CMYY+DNwTtRMBSJCAwgyBNAI+HFygwEQoJ4EQwQpEHwwuVGAhPFGwIABFIY+GFywwDdoTAFe4ZgCFzjDFSAy4NFyowIF4PIF0gwHXAKYGFz4wHXBgubGAxeOFzT1KFsowQF0AwNF0QxKFsoxIEj/XGBgQDCJYrODQICHEgQDGAQoSECwhaNCoYJDAwgoHBxBfVJgpfJGIgOHL5haGPgoBDTxIBHAH4AkA==")) From dd82b80b86fa8fc7eaac5c29e4da82f7f77a00a0 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:35:39 +0100 Subject: [PATCH 07/70] Update app-icon.js --- apps/stressless/app-icon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stressless/app-icon.js b/apps/stressless/app-icon.js index 0dd5ec32a..0d9739661 100644 --- a/apps/stressless/app-icon.js +++ b/apps/stressless/app-icon.js @@ -1 +1 @@ -E.toArrayBuffer(atob("mEwxH+AH4A/AH4A/AFW7AAgThDBQeNCaYaSDg4TTFygcFCaYuWDgYTTFyHB5AmRGCARK4XJF7bXR5PQF9vQ6/CF8COL6/XYDgwERxYvBYEKOM67AhLQwEDFwTAGYywvIXIIhCAgPQSILAE4RmWF5AnBMAQECA4gJDMCovDGAfBQ4PR4K+C4RiCZAr3UFwgvDJ4IAB5IrEYAgJBHwaoF6HQNRIvFEQItDXYaTEYAQ+EAogADZZIvFGAYTBAgIBBFQg0CRwQREF5wuGeQpNDQYSUDAYYyFR5guIF4jyCdQ3CMYY+DNwTtRMBSJCAwgyBNAI+HFygwEQoJ4EQwQpEHwwuVGAhPFGwIABFIY+GFywwDdoTAFe4ZgCFzjDFSAy4NFyowIF4PIF0gwHXAKYGFz4wHXBgubGAxeOFzT1KFsowQF0AwNF0QxKFsoxIEj/XGBgQDCJYrODQICHEgQDGAQoSECwhaNCoYJDAwgoHBxBfVJgpfJGIgOHL5haGPgoBDTxIBHAH4AkA==")) +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AFW7AAgThDBQeNCaYaSDg4TTFygcFCaYuWDgYTTFyHB5AmRGCARK4XJF7bXR5PQF9vQ6/CF8COL6/XYDgwERxYvBYEKOM67AhLQwEDFwTAGYywvIXIIhCAgPQSILAE4RmWF5AnBMAQECA4gJDMCovDGAfBQ4PR4K+C4RiCZAr3UFwgvDJ4IAB5IrEYAgJBHwaoF6HQNRIvFEQItDXYaTEYAQ+EAogADZZIvFGAYTBAgIBBFQg0CRwQREF5wuGeQpNDQYSUDAYYyFR5guIF4jyCdQ3CMYY+DNwTtRMBSJCAwgyBNAI+HFygwEQoJ4EQwQpEHwwuVGAhPFGwIABFIY+GFywwDdoTAFe4ZgCFzjDFSAy4NFyowIF4PIF0gwHXAKYGFz4wHXBgubGAxeOFzT1KFsowQF0AwNF0QxKFsoxIEj/XGBgQDCJYrODQICHEgQDGAQoSECwhaNCoYJDAwgoHBxBfVJgpfJGIgOHL5haGPgoBDTxIBHAH4AkA==")) From 8a4be166af798326319ccd7139ae7017561c4e96 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:48:26 +0100 Subject: [PATCH 08/70] Create ChangeLog --- apps/stressless/ChangeLog | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/stressless/ChangeLog diff --git a/apps/stressless/ChangeLog b/apps/stressless/ChangeLog new file mode 100644 index 000000000..55ebb3e4b --- /dev/null +++ b/apps/stressless/ChangeLog @@ -0,0 +1 @@ +0.01: New App From 0b2dfae94fe353880b72d25ffea69946367a64f9 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:54:24 +0100 Subject: [PATCH 09/70] Update app.js --- apps/stressless/app.js | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/apps/stressless/app.js b/apps/stressless/app.js index b5e0ba8dd..2caf7ac40 100644 --- a/apps/stressless/app.js +++ b/apps/stressless/app.js @@ -1,9 +1,9 @@ var option = null; //debugging or analysis files -var logfile = require("Storage").open("HRV_log.csv", "w"); +//var logfile = require("Storage").open("HRV_log.csv", "w"); -logfile = require("Storage").open("HRV_log.csv", "a"); +var logfile = require("Storage").open("HRV_log.csv", "a"); var csv = [ "time", @@ -16,11 +16,9 @@ var csv = [ ]; logfile.write(csv.join(",")+"\n"); -var debugging = true; var samples = 0; // how many samples have we connected? var collectData = false; // are we currently collecting data? -var BPM_array = []; var raw_HR_array = new Float32Array(1536); var alternate_array = new Float32Array(3072); var pulse_array = []; @@ -43,12 +41,6 @@ function storeMyData(data, file_type) { "ram" function average(samples) { return E.sum(samples) / samples.length; // faster builtin - /* var sum = 0; - for (var i = 0; i < samples.length; i++) { - sum += parseFloat(samples[i]); - } - var avg = sum / samples.length; - return avg;*/ } function StandardDeviation (array) { @@ -216,25 +208,11 @@ function calculate_HRV() { movement.toFixed(5) ]; logfile.write(csv.join(",")+"\n"); - - - // for (let i = 0; i < raw_HR_array.length; i++) { - // raw_HR_array[i] = null; - //} - turn_on(); } } -function btn1Pressed() { - if(option === null){ - g.clear(); - g.drawString("one-off assessment", px, py); - option = 0; - turn_on(); - } -} function btn3Pressed() { if(option === null){ @@ -248,7 +226,7 @@ function btn3Pressed() { } function turn_on() { - BPM_array = []; + pulse_array = []; samples = 0; if (accel) clearInterval(accel); @@ -265,7 +243,6 @@ function drawButtons() { g.setFont("6x8", 2); g.setFontAlign(-1,1); g.drawString("continuous", 120, 210); - g.drawString("one-time", 140, 50); g.setColor("#ffffff"); g.setFontAlign(0, 0); } @@ -277,13 +254,10 @@ drawButtons(); g.setFont("6x8", 2); g.setColor("#ffffff"); g.setFontAlign(0, 0); // center font -g.drawString("check app README\nfor more info", px, py); -setWatch(btn1Pressed, BTN1, {repeat:true}); setWatch(btn3Pressed, BTN3, {repeat:true}); - Bangle.on('HRM-raw', function (e) { if (!collectData) return; storeMyData(e.raw, 0); From a8ddde48dbee7baadcfcb30eb6a859eabf7c3ea2 Mon Sep 17 00:00:00 2001 From: hanna0616 <64650462+hanna0616@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:14:02 +0100 Subject: [PATCH 10/70] Update app.js --- apps/stressless/app.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/stressless/app.js b/apps/stressless/app.js index 2caf7ac40..b27c79a0d 100644 --- a/apps/stressless/app.js +++ b/apps/stressless/app.js @@ -3,7 +3,7 @@ var option = null; //debugging or analysis files //var logfile = require("Storage").open("HRV_log.csv", "w"); -var logfile = require("Storage").open("HRV_log.csv", "a"); +var logfile = require("Storage").open("HRV_logs.csv", "a"); var csv = [ "time", @@ -16,9 +16,11 @@ var csv = [ ]; logfile.write(csv.join(",")+"\n"); +var debugging = true; var samples = 0; // how many samples have we connected? var collectData = false; // are we currently collecting data? +var BPM_array = []; var raw_HR_array = new Float32Array(1536); var alternate_array = new Float32Array(3072); var pulse_array = []; @@ -41,6 +43,12 @@ function storeMyData(data, file_type) { "ram" function average(samples) { return E.sum(samples) / samples.length; // faster builtin + /* var sum = 0; + for (var i = 0; i < samples.length; i++) { + sum += parseFloat(samples[i]); + } + var avg = sum / samples.length; + return avg;*/ } function StandardDeviation (array) { @@ -208,12 +216,13 @@ function calculate_HRV() { movement.toFixed(5) ]; logfile.write(csv.join(",")+"\n"); + + turn_on(); } } - function btn3Pressed() { if(option === null){ logfile.write(""); //reset HRV log @@ -226,7 +235,7 @@ function btn3Pressed() { } function turn_on() { - + BPM_array = []; pulse_array = []; samples = 0; if (accel) clearInterval(accel); @@ -242,7 +251,7 @@ function drawButtons() { g.setColor("#00ff7f"); g.setFont("6x8", 2); g.setFontAlign(-1,1); - g.drawString("continuous", 120, 210); + g.drawString("start recording HRV", 120, 210); g.setColor("#ffffff"); g.setFontAlign(0, 0); } @@ -258,6 +267,7 @@ g.setFontAlign(0, 0); // center font setWatch(btn3Pressed, BTN3, {repeat:true}); + Bangle.on('HRM-raw', function (e) { if (!collectData) return; storeMyData(e.raw, 0); From 950f5b79d6b86c7e8562fdd35e34e71afbcc075e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Gonz=C3=A1lez?= Date: Thu, 4 Jan 2024 17:53:44 +0100 Subject: [PATCH 11/70] step second by second --- apps/intervals/intervals.app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/intervals/intervals.app.js b/apps/intervals/intervals.app.js index da436b104..db20818ad 100644 --- a/apps/intervals/intervals.app.js +++ b/apps/intervals/intervals.app.js @@ -149,9 +149,9 @@ function showMenu() "START" : function() { startSession(); }, "Sets" : { value : settings.sets,min:0,max:20,step:1,onchange : v => { settings.sets=v; } }, "Work minutes" : { value : settings.workmin,min:0,max:59,step:1,onchange : v => { settings.workmin=v; } }, - "Work seconds" : { value : settings.workseg,min:0,max:59,step:5,onchange : v => { settings.workseg=v; } }, + "Work seconds" : { value : settings.workseg,min:0,max:59,step:1,onchange : v => { settings.workseg=v; } }, "Rest minutes" : { value : settings.restmin,min:0,max:59,step:1,onchange : v => { settings.restmin=v; } }, - "Rest seconds" : { value : settings.restseg,min:0,max:59,step:5,onchange : v => { settings.restseg=v; } }, + "Rest seconds" : { value : settings.restseg,min:0,max:59,step:1,onchange : v => { settings.restseg=v; } }, "Signal type" : { value : settings.buzz,format : v => v?"Buzz":"Beep",onchange : v => { settings.buzz=v; }} }; From a1968d49360efa6c851885123b066589786ed276 Mon Sep 17 00:00:00 2001 From: pglez82 Date: Thu, 4 Jan 2024 18:05:15 +0100 Subject: [PATCH 12/70] adding new constelations to planetarium --- apps/planetarium/ChangeLog | 1 + apps/planetarium/planetarium.const.csv | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/apps/planetarium/ChangeLog b/apps/planetarium/ChangeLog index 9eedad602..9ef1f5c5b 100644 --- a/apps/planetarium/ChangeLog +++ b/apps/planetarium/ChangeLog @@ -2,3 +2,4 @@ 0.02: Major speed improvement. Added more stars. Up to 500! 0.03: Added more stars and constellations. Now it shows 20 constellations. 0.04: Use default Bangle formatter for booleans +0.05: Added more constellations (scorpio and aguila) diff --git a/apps/planetarium/planetarium.const.csv b/apps/planetarium/planetarium.const.csv index 972e4faff..8fb5466b2 100644 --- a/apps/planetarium/planetarium.const.csv +++ b/apps/planetarium/planetarium.const.csv @@ -38,3 +38,7 @@ Draco e_15 131,131 70,70 382,382 e_15,382 187,187 423,423 e_16,e_16 207,207 122,122 e_17,e_17 232,232 342,342 452,452 428 Pegasus 92 85,138 54,54 85,138 92,283 85,283 389,160 85,92 258,258 297,297 83 +Aguila +12 249,249 271,249 170,249 217,12 365,120 12 +Scorpius +14 105,14 80,14 152,14 137,137 76,332 239,239 41,76 188,188 332,41 181,181 27 From f9eaf70548ccca61731ada9d800b58f3c77542ca Mon Sep 17 00:00:00 2001 From: pglez82 Date: Thu, 4 Jan 2024 18:08:26 +0100 Subject: [PATCH 13/70] updating version in metadata.json --- apps/planetarium/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/planetarium/metadata.json b/apps/planetarium/metadata.json index 78add2cea..a0327b842 100644 --- a/apps/planetarium/metadata.json +++ b/apps/planetarium/metadata.json @@ -2,7 +2,7 @@ "id": "planetarium", "name": "Planetarium", "shortName": "Planetarium", - "version": "0.04", + "version": "0.05", "description": "Planetarium showing up to 500 stars using the watch location and time", "icon": "planetarium.png", "tags": "", From cbcd25c4a24c5b7d3ece83cefa63f4d20ec25513 Mon Sep 17 00:00:00 2001 From: pglez82 Date: Thu, 4 Jan 2024 19:16:42 +0100 Subject: [PATCH 14/70] quote clock --- apps/quoteclock/app-icon.js | 1 + apps/quoteclock/app.js | 34 ++++++++++++++++++++++++++++++++++ apps/quoteclock/metadata.json | 15 +++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 apps/quoteclock/app-icon.js create mode 100644 apps/quoteclock/app.js create mode 100644 apps/quoteclock/metadata.json diff --git a/apps/quoteclock/app-icon.js b/apps/quoteclock/app-icon.js new file mode 100644 index 000000000..5dcc91801 --- /dev/null +++ b/apps/quoteclock/app-icon.js @@ -0,0 +1 @@ +E.toArrayBuffer(atob("MDAEEREREREREURERERERERERBERERERERERERERERERREREREREREREREEREREREREREREREREURERERERERERERERBERERERERERERERFEREREREREREREREREERERERERERERERREREREREREREREREREQREREREREREREURBEhEURERERERERERERBEREREREREREURBESERIkRERERERERERBERERERERERFEQhERIhEiREREREREREREERERERERERFERCERIiIiJEREREREREREQRERERERERREREERASIiEkREREREREREQRERERERERREREQQERIiEkRERERERERERBEREREREUREREQRERAiIiEkRERERERERBEREREREURERERAERERISEkRERERERERBEREREREUREREREEQAREiJEREREREREREERERERFEREREREIiIRESIUREREREREREERERERFEREREREERABERIRREREREREREERERERFEREREREQREREREhREREREREREERERERFEREREREIiJBERESFEREREREREQRERERFEREREREIiQRERERIUREREREREQRERERRERERERCIiIhEQEREhREREREREQRERERRERERERCIiIiIRERESFEREREREQRERERREREREQiIiIiIiQRESJEREREREQRERERREREREQiIiIiIiIkFBJEREREREQRERERREREREQiIiIiIiJBESEkREREREQRERERREREREQiIiIiIiIiRCIiREREREQRERERREREREQiIiIiIiIiIkQiREREREQRERERREREREQiIiIiIiIiIiIiJEREREQRERERREREREQiIiIiIyIiIiIiIkREREQRERERREREREQiIiIkQjMiIiIkIkREREQRERERREREREREREREQiIiIiJEQiIiMkQRERERREQiIkREQRESIiIiIiJCQiMzIkQRERERREIiIgAAABEBERRCMzMzIiIhARERERERFEIiIiIiIiIzMzMzMzMhAAJAEQVRERERQjMzMzMzMzMzMiIhEAAAAAEiBURBERFCIzMzIiIhERAAAAARERERERESFERBEREUERAAAAAAAAARERERERERFREUJEQhERERERFEREREREREREREREREREREJEQkEREREUREREREREREREREREREREREEkREERERFEREREREREREREREQiJEREREQkRBERERFEJERCIiRCIiIiJEIiIkREREQSERARERESREIiIiIiRERERERERERCREVSIAEREREUREREREREREREREREREREREVREBEREREURERERBFERERERERERERERVEAAREREREQAAABEREREREREUREREREEAAAAAABEREREREREREREREREAAAAAAAAAAAABERERERERERERRERERERBEREREREREREREREREREREREUREREREREREQRERERERERERERERERERERRERERERERBERERERERERER")) \ No newline at end of file diff --git a/apps/quoteclock/app.js b/apps/quoteclock/app.js new file mode 100644 index 000000000..c4ec0e7da --- /dev/null +++ b/apps/quoteclock/app.js @@ -0,0 +1,34 @@ +const locale=require("locale"); +const quotesshakespeare=["Be not afraid of greatness. Some are born great, some achieve greatness, and others have greatness thrust upon them.","We know what we are, but know not what we may be.","Sweet are the uses of adversity which, like the toad, ugly and venomous, wears yet a precious jewel in his head.","Our doubts are traitors and make us lose the good we oft might win by fearing to attempt.","Give every man thy ear, but few thy voice.","Uneasy lies the head that wears the crown.","How poor are they that have not patience! What wound did ever heal but by degrees?","Nothing can come of nothing.","How far that little candle throws its beams! So shines a good deed in a naughty world.","What's done can't be undone.","Though she be but little, she is fierce.","No legacy is so rich as honesty.","This above all; to thine own self be true.","I wasted time, and now doth time waste me.","The robbed that smiles, steals something from the thief.","The devil can cite Scripture for his purpose.","One touch of nature makes the whole world kin.","What is past is prologue.","Small cheer and great welcome makes a merry feast.","Sweet mercy is nobility's true badge.","'Tis not enough to help the feeble up, but to support them after.","Neither a borrower nor a lender be.","Ambition should be made of sterner stuff.","I bear a charmed life.","Heat not a furnace for your foe so hot that it do singe yourself.","Talking isn't doing. It is a kind of good deed to say well; and yet words are not deeds.","In time we hate that which we often fear.","Modest doubt is called the beacon of the wise.","With mirth and laughter let old wrinkles come.","Boldness be my friend.","Words without thoughts never to heaven go.","Wisely, and slow. They stumble that run fast.","Pleasure and action make the hours seem short.","When words are scarce they are seldom spent in vain.","Such as we are made of, such we be.","And oftentimes excusing of a fault doth make the fault the worse by the excuse.","Reputation is an idle and most false imposition; oft got without merit, and lost without deserving.","To be, or not to be: that is the question.","All the world's a stage, and all the men and women merely players. They have their exits and their entrances; and one man in his time plays many parts.","All that glisters is not gold.","Words are easy, like the wind; faithful friends are hard to find.","The fault is not in our stars, but in ourselves.","And this, our life, exempt from public haunt, finds tongues in trees, books in the running brooks, sermons in stones, and good in everything.","Expectation is the root of all heartache.","I like this place and could willingly waste my time in it.","Better three hours too soon than a minute too late.","Life's but a walking shadow, a poor player, that struts and frets his hour upon the stage, and then is heard no more; it is a tale told by an idiot, full of sound and fury, signifying nothing.","My tongue will tell the anger of my heart, or else my heart concealing it will break.","Brevity is the soul of wit.","Give sorrow words; the grief that does not speak knits up o-er wrought heart and bids it break.","Look like the innocent flower, but be the serpent under it.","One may smile, and smile, be a villain.","Conscience doth make cowards of us all.","Let me be that I am and seek not to alter me.","Et tu, Brute?","O, beware, my lord, of jealousy; it is the green-ey'd monster, which doth mock the meat it feeds on.","If we are true to ourselves, we can not be false to anyone.","Be great in act, as you have been in thought.","Suspicion always haunts the guilty mind.","All things are ready, if our mind be so.","Many a true word hath been spoken in jest.","For sweetest things turn sourest by their deeds; lillies that fester smell far worse than weeds.","The Devil hath power to assume a pleasing shape.","Thought is free.","April hath put a spirit of youth in everything.","Summer's lease hath all too short a date.","Our bodies are our gardens to the which our wills are gardeners.","The tempter or the tempted, who sins most?","Men should be what they seem.","He jests at scars that never felt a wound.","I would not wish any companion in the world but you.","Self-love, my liege, is not so vile a sin, as self-neglecting.","Doubt thou the stars are fire, doubt that the sun doth move. Doubt truth to be a liar, but never doubt I love.","I am one who loved not wisely but too well.","A young woman in love always looks like patience on a monument smiling at grief.","My bounty is as boundless as the sea, my love as deep; the more I give to thee, the more I have, for both are infinite.","They do not love that do not show their love.","I love you with so much of my heart that none is left to protest.","Love is heavy and light, bright and dark, hot and cold, sick and healthy, asleep and awake.","Shall I compare thee to a summer's day? Thou art more lovely and more temperate.","Love all, trust a few, do wrong to none.","Kindness in women, not their beauteous looks, shall win my love.","Love looks not with the eyes, but with the mind, and therefore is winged Cupid painted blind.","Do not swear by the moon, for she changes constantly. Then your love would also change.","If music be the food of love, play on.","Love is too young to know what conscience is.","Did my heart love till now? Forswear it, sight! For I ne'er saw true beauty till this night.","Don't waste your love on somebody, who doesn't value it.","And yet, to say the truth, reason and love keep little company together nowadays.","Love is a smoke made with the fume of sighs.","Go to your bosom: Knock there, and ask your heart what it doth know.","In black ink my love may still shine bright.","Love alters not with his brief hours and weeks, but bears it out even to the edge of doom.","See how she leans her cheek upon her hand. O, that I were a glove upon that hand that I might touch that cheek!","The course of true love never did run smooth.","Love sought is good, but given unsought, is better.","For which of my bad parts didst thou first fall in love with me?","Speak low, if you speak love.","Love comforteth like sunshine after rain.","Good night, good night! Parting is such sweet sorrow, that I shall say good night till it be morrow.","So long as men can breathe or eyes can see, so long lives this and this gives life to thee.","For you, in my respect, are all the world.","Love is merely a madness.","Love is not love which alters when it alteration finds.","How art thou out of breath when thou hast breath to say to me that thou art out of breath?","I wish my horse had the speed of your tongue.","Do you not know I am a woman? When I think, I must speak.","I had rather hear my dog bark at a crow, than a man swear he loves me.","'I can see that he's not in your good books,' said the messenger. 'No, and if he were I would burn my library.'","God has given you one face, and you make yourself another.","Misery acquaints a man with strange bedfellows.","He that loves to be flattered is worthy o' the flatterer.","Life is as tedious as twice-told tale, vexing the dull ear of a drowsy man.","Maids want nothing but husbands, and when they have them, they want everything.","O thou invisible spirit of wine, if thou hast no name to be known by, let us call thee devil.","Lord, what fools these mortals be!","I will praise any man that will praise me.","My pride fell with my fortunes.","Better a witty fool than a foolish wit.","Is it not strange that desire should so many years outlive performance?","I dote on his very absence.","There's many a man has more hair than wit.","Cowards die many times before their deaths; the valiant never taste of death but once.","A fool thinks himself to be wise, but a wise man knows himself to be a fool.","I am not bound to please thee with my answer."]; +const quotesjane=["The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.","Friendship is certainly the finest balm for the pangs of disappointed love","Give a loose rein to your fancy, indulge your imagination in every possible flight which the subject will afford","There is nothing I would not do for those who are really my friends. I have no notion of loving people by halves, it is not my nature.","I declare after all there is no enjoyment like reading! How much sooner one tires of anything than of a book!","In vain have I struggled. It will not do. My feelings will not be repressed. You must allow me to tell you how ardently I admire and love you.","It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.","I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation. It is too long ago. I was in the middle before I knew that I had begun.","Angry people are not always wise.","I hate to hear you talk about all women as if they were fine ladies instead of rational creatures. None of us want to be in calm waters all our lives.","Vanity and pride are different things, though the words are often used synonymously.","You pierce my soul. I am half agony, half hope. Tell me not that I am too late, that such precious feelings are gone for ever.","There is a stubbornness about me that never can bear to be frightened at the will of others. My courage always rises at every attempt to intimidate me.","What are men to rocks and mountains?","I could easily forgive his pride, if he had not mortified mine.","Ah! There is nothing like staying at home, for real comfort.","If I loved you less, I might be able to talk about it more.","I always deserve the best treatment because I never put up with any other.","My good opinion once lost is lost forever.","There could have been no two hearts so open, no tastes so similar, no feelings so in unison","Nothing ever fatigues me, but doing what I do not like.","I wish, as well as everybody else, to be perfectly happy; but, like everybody else, it must be in my own way.","There are people, who the more you do for them, the less they will do for themselves.","One half of the world cannot understand the pleasures of the other.","...when pain is over, the remembrance of it often becomes a pleasure.","Without music, life would be a blank to me.","You must be the best judge of your own happiness.","If adventures will not befall a young lady in her own village, she must seek them abroad.","I am only resolved to act in that manner, which will, in my own opinion, constitute my happiness, without reference to you, or to any person so wholly unconnected with me.","I am excessively diverted.","We have all a better guide in ourselves, if we would attend to it, than any other person can be.","A fondness for reading, properly directed, must be an education in itself.","Indeed, I am very sorry to be right in this instance. I would much rather have been merry than wise.","We do not suffer by accident.","Anne hoped she had outlived the age of blushing; but the age of emotion she certainly had not.","Do not let the behaviour of others destroy your inner peace.","There are as many forms of love as there are moments in time.","Indulge your imagination in every possible flight.","Obstinate, headstrong girl!","Why not seize the pleasure at once? -- How often is happiness destroyed by preparation, foolish preparation!","Is not general incivility the very essence of love?","She had a lively, playful disposition that delighted in anything ridiculous.","Now I must give one smirk, and then we may be rational again.","It is not what we think or feel that makes us who we are. It is what we do. Or fail to do...","Money can only give happiness where there is nothing else to give it.","I was so anxious to do what is right that I forgot to do what is right."]; +const quotesother=["The cure for anything is salt water, sweat, tears, or the salt sea.","Stuff your eyes with wonder. Live as if you'd drop dead in ten seconds. See the world.","It's opener, out there, in the wide, open air.","Oh, the places you'll go!","A little magic can take you a long way.","Most of the really exciting things we do in our lives scare us to death. They wouldn't be exciting if they didn't.","Somewhere inside all of us is the power to change the world.","if you have good thoughts they will shine out of your face like sunbeams and you will always look lovely.","I understand what you're saying, and your comments are valuable, but I'm gonna ignore your advice.","Meanings is not important, said the BFG. I cannot be right all the time. Quite often I is left instead of right.","It doesn't matter who you are or what you look like, so long as somebody loves you.","A little nonsense now and then is relished by the wisest men.","But in a solitary life, there are rare moments when another soul dips near yours, as stars once a year brush the earth. Such a constellation was he to me.","He showed me his scars, and in return he let me pretend that I had none.","Humbling women seems to me a chief pastime of poets. As if there can be no story unless we crawl and weep.","I thought: I cannot bear this world a moment longer. Then, child, make another.","Only that: we are here. This is what it means to swim in the tide, to walk the earth and feel it touch your feet. This is what it means to be alive.","I will not be like a bird bred in a cage, I thought, too dull to fly even when the door stands open.","The truth is, men make terrible pigs.","It was my first lesson. Beneath the smooth, familiar face of things is another that waits to tear the world in two.","As you set out for Ithaka, hope your road is a long one, Keep Ithaka always in your mind. Arriving there is what you're destined for. But don't hurry the journey.","It takes a great deal of bravery to stand up to our enemies, but just as much to stand up to our friends.","If you want to know what a man's like, take a good look at how he treats his inferiors, not his equals.","When in doubt, go to the library.","It is our choices, Harry, that show what we truly are, far more than our abilities.","Don't worry. You're just as sane as I am.","I have hated words and I have loved them, and I hope I have made them right.","Like most misery, it started with apparent happiness.","A DEFINITION NOT FOUND IN THE DICTIONARY: Not leaving: an act of trust and love, often deciphered by children","One was a book thief. The other stole the sky.","In his blue gardens men and girls came and went like moths among the whisperings and the champagne and the stars.","So we beat on, boats against the current, borne back ceaselessly into the past.","He looked at her the way all women want to be looked at by a man.","The loneliest moment in someone's life is when they are watching their whole world fall apart, and all they can do is stare blankly.","Whenever you feel like criticizing any one...just remember that all the people in this world haven't had the advantages that you've had.","In the midst of winter, I found there was, within me, an invincible summer. And that makes me happy.","And in me too the wave rises. It swells; it arches its back. I am aware once more of a new desire","Lock up your libraries if you like; but there is no gate, no lock, no bolt that you can set upon the freedom of my mind.","One cannot think well, love well, sleep well, if one has not dined well.","People who love to eat are always the best people","Why, sometimes I've believed as many as six impossible things before breakfast.","It's no use going back to yesterday, because I was a different person then.","To live is the rarest thing in the world. Most people exist, that is all.","We are all in the gutter, but some of us are looking at the stars.","The truth is rarely pure and never simple.","Never love anyone who treats you like you're ordinary.","Not all those who wander are lost","When spring came, even the false spring, there were no problems except where to be happiest.","I don't want comfort. (...) I want poetry. I want danger. I want freedom. I want goodness. I want sin.","Morning without you is a dwindled dawn.","I took a deep breath and listened to the old brag of my heart. I am, I am, I am.","Earth laughs in flowers","I felt my lungs inflate with the onrush of scener air, mountains, trees, people. I thought, This is what it is to be happy.","She never went astray, never made an error. I had been jealous then. Now I thought: what a burden. What an ugly weight upon your back.","Live in the sunshine, swim the sea, drink the wild air.","I felt once more how simple and frugal a thing is happiness: a glass of wine, a roast chestnut, a wretched little brazier, the sound of the sea. Nothing else.","I will clamber through the Clouds and exist.","Just living isn't enough, said the butterfly, one must have sunshine, freedom and a little flower.","It is a serious thing, to be alive, on this fresh morning, in this broken world","We loved with a love that was more than love","To young men contemplating a voyage I would say, go.","Encuéntrate y pon la serendipia del revés. Haz del mundo tu pista de baile.","I discover myself on the verge of a usual mistake","I too am not a bit tamed I too am untranslatable; I sound my barbaric yawp over the roofs of the world.","Do I contradict myself? Very well then, I contradict myself; I am large, I contain multitudes.","No need to hurry. No need to sparkle. No need to be anybody but oneself.","Procura guardar siempre por encima de tu vida un buen espacio de cielo.","El mundo hay que fabricárselo uno mismo, hay que crear peldanos que te suban, que te saquen del pozo. Hay que inventar la vida porque acaba siendo verdad","En vista de lo visto me desvisto, me desnudo a mí misma y me mantengo, me encanta este tener lo que no tengo","Lo mejor del olvido es el recuerdo","I suggest we learn to love ourselves before it's made illegal","To unpathed waters, undreamed shores.",]; +var skullleft={width:32,height:40,bpp:4,buffer:require("heatshrink").decompress(atob("AAcBrXf//sygOJz//AAfmB48eBwgAB4QPGv4PG/8QB4vvB49gBwkDBw//0APEhQPI6APEt4PI+AODgX/23P/eOB5MM/EA1/xMYoGBAAU/wBQBHAMDMgYPEh8wl35+YGBlxPHhOk7+T/RlF2BPF/kSTIRlDyAPDgomC//pxRPDqAPDhqWC36/Kn4fCeAv9TwmP//5HYXfB4XlB4ldqQKC+l5AYO0dosIwwPC+f25+CBwusmn6FYX1/9AgEwB4ku/G/q39//2EINt/IPEgS4B+VQ7nJOIYiBN4v9qMRidPXwcC5yfEAAOSk2eMgcvQIlOTgoAB/e/B4kB14PG/ihGgZSBAAfJxYPGKQQADYgYPFgNZ5vFif/54PIAAnPB4X6UYoAEpRFB/19+wOIgwtCzICB6oPHr4LB+4xC/APHg4LBtwiCyAwInn3BwX3MBVvBwP9L5QPDwIOKgG/BxsAxdQAoY="))}; +var skullright={width:32,height:40,bpp:4,buffer:require("heatshrink").decompress(atob("AAtJ53//uliAOIt3/AAf5CA8sBwgAB8IOFgIOG//3B4sGB4/+B4sKB4//mAPEhoPI0APEh4PI+wPK9G//lr/8gB4cfLYsf/UAx/AB4/+JQJGBmEI+ZPH8QGBn+f8UD+APDhZZF185/tKyAPDhJZEUoMh55PFgpPD1Ge/44CqC/K/aoC6CAE77uFD4RfEgFeBoX9IYWf//4aItLBgOXp4UCkvVB4sAlH8t8/B4VowAOFgcAgn/r4zC19D5QPEz/WDoNv//fsv7x6nCAAIcBNoWc5sFl7GBSAXskDAD+kxiMVO4JvD7/yLQfpsUpAoSfD7/736gFAAPkTorHBAAv6iAPF3GcBwn+mBsEXQYPEXgoPD/n/mNc7mVFoibB14PC/i3GAAVv6/vHIOkBxFdDoOZGIVgB4+PFoV/AYP1B48JDgXmAQNwGBAcC/9/4ZPJgaPB//2BxIABjAPOCAX7B5kAgu4BZI="))}; +var janeright={width:32,height:40,bpp:1,transparent:-1,palette:new Uint16Array([0,65535]),buffer:require("heatshrink").decompress(atob("AAM8AQMD/4DBg//4EAh//8EAj//+ADC/ALC/wDGDgIPBA4IDF/gjEAYIrDwADCG4ZDCv4LBgE/AYYXBAYIfBB4IDDI4IDFJYMAG4UBH4RnBAYIvBK4pPHNYYDBKYobBIYMDCwIDBCwIDBHQUDBwI"))}; +var janeleft={width:32,height:40,bpp:1,transparent:-1,palette:new Uint16Array([0,65535]),buffer:require("heatshrink").decompress(atob("gE8AYX/4EAgf/8EAg//+EAh///EAj4DCv4LCAYf/A4gPDAYM/AYQjDFYIDBgIDCDgIDDG4MB/wGBgP8AYMDAYUHAYYPCh4DGj4DCG4IDBH4OAAYRnBAYIvBLYpPFNYZTBAYJTFDYIYBEYIYBAYUPAYUBAYI="))}; +var bookleft={width:32,height:40,bpp:4,transparent:-1,palette:new Uint16Array([0,52857,2113,65535,4258,32,25388,29614,4226,6371,48631,16936,2145,8452,12678,52825]),buffer:require("heatshrink").decompress(atob("AH8iAC9VlFJ7vu9IMGlHd9e2s931WjigPGxWq1/xiIACtAPGsYMDAAMTygOFlsxB4sXhOCB4knBwsRn0I9gODku/B42ihviB4cpuI+G2EO6QPDlY+GjXQk3CB4efFw0XkHbHwlzB437gFkB4cuFw0R1MMuCcEBw0RuEGJwmxBw0T6FXTgmhB40agHuB4cmHw87gDOE6I+H/EE3htDTgw+BssOVodbFw8R8FbyQPCzQOHi+Aww+DsIPHixeENpER1sAuQPC40a1Wv/4ABTocNsoPC5eczue92721nu938EHJwcp2EAAAkFrMAo9SJ4fWoAQFgkA3wODAAO3u92tfu7KXBzCtEAANQgGE7vrIANms2yB4oAEktVlGJwQPKABBMFAH4AJA="))}; +var bookright={width:32,height:40,bpp:4,transparent:-1,palette:new Uint16Array([0,52857,2113,65535,4258,32,25388,29614,4226,6371,48631,16936,2145,8452,12678,52825]),buffer:require("heatshrink").decompress(atob("AH8iAB+e93dymCqoPJoM61V3u1r33dwQPGw0RAAUf/Wq1APGpMxCAYABmwOFlGQuIPFifSB4nOwHjB4sRuQPEl3QlQPG/dSB4ct8ELIA0XyQPDllihuhIA2yGAm9gRQGiP5B4lGgG/B403IAkH4GaB40T8RQEsEHB40RUQt1hpgGiMbB4nugBgGiMaUQmGgGzIA9iB4fLoGPIA8dWIngqxAHi5xDlO1hwfHie1EAdohChHiOpMAthB48WB4cngHaB45xEq3QUIf/AAP61WhthQDuEOu93u1r3fu9PZ5O8B4UlukAytQgAAEheSIAfrgFABwsEtrBEw2JQ4Od922s93vYOEkUrs1mHwO+7tIgEFB4sozGCqqpDACZIFAH4AJA"))}; +let hour; +let minute; +let date; +let timer; +const watchtop=55; +const hoursize=32; +const datesize=14; +let qnumber; +let quotehour; +let qtype; +let quotes; +let imageleft=bookleft; +let imageright=bookright; +function draw(){const d=new Date();const newHour=('0'+d.getHours()).substr(-2);const newMinute=('0'+d.getMinutes()).substr(-2);const newDate=locale.date(d).trim();g.setFontAlign(0,0,0);if(newHour !== hour){g.setFont("Vector",hoursize);g.setColor(0x0000);g.drawString(hour,60,watchtop);g.setColor(0xFFFF);g.drawString(newHour,60,watchtop);hour=newHour;drawQuote();}g.setFontAlign(0,0,0);if(newMinute !== minute){g.setFont("Vector",hoursize);g.setColor(0x0000);g.drawString(minute,120,watchtop);g.setColor(0xFFFF);g.drawString(newMinute,120,watchtop);minute=newMinute;}g.setFontAlign(0,0,0);if(newDate !== date){g.setFont("Vector",datesize);g.setColor(0x0000);g.drawString(date,88,168);g.setColor(0xFFFF);g.drawString(newDate,88,168);date=newDate;}} +function drawStrCenter(str,colour){g.setColor(colour);g.setFont("Vector",13);str="\""+str+"\"";maxLength=24;var ta=[];let index=0;let linestart=0;while(indexmaxLength){ta.push(str.substring(linestart,index));ta.push(str.substring(index,str.length));}else ta.push(str.substring(linestart,str.length));break;}else if(((newIndex-linestart)>maxLength)){ta.push(str.substring(linestart,index));linestart=index;}else index=newIndex+1;}y=110-ta.length*5;ta.forEach((e)=>{g.setFontAlign(0,-1).drawString(e.trim(),88,y);y += 12;});} +function drawQuote(){if(qnumber == undefined || quotehour != hour){if(qnumber != undefined)drawStrCenter(quotes[qnumber],0x0000);qtype=Math.floor(Math.random()*3);if(qtype === 0){quotes=quotesshakespeare;imageleft=skullleft;imageright=skullright;}if(qtype === 1){quotes=quotesjane;imageleft=janeleft;imageright=janeright;}if(qtype === 2){quotes=quotesother;imageleft=bookleft;imageright=bookright;}drawImages();qnumber=Math.floor(Math.random()*quotes.length);quotehour=hour;}drawStrCenter(quotes[qnumber],0xFFFF);} +function drawImages(){g.drawImage(imageleft,0,30);g.drawImage(imageright,142,30);} +function startDrawing(){hour='';minute='';date='';g.setColor(0X0000);g.fillRect(0,0,176,176);g.setFontAlign(0,0,0);g.setFont("Vector",hoursize);g.setColor(0xFFFF);g.drawString(":",88,watchtop);drawImages();console.log("drawing image");Bangle.drawWidgets();draw();timer=setInterval(draw,1000);} +function stopDrawing(){if(timer){clearInterval(timer);timer=undefined;}} +g.clear(); +Bangle.on('lcdPower',(on)=>{stopDrawing();if(on){startDrawing();}}); +Bangle.loadWidgets(); +startDrawing(); +Bangle.setUI("clock"); \ No newline at end of file diff --git a/apps/quoteclock/metadata.json b/apps/quoteclock/metadata.json new file mode 100644 index 000000000..21220f173 --- /dev/null +++ b/apps/quoteclock/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "quoteclock", + "name": "Quote Clock", + "version": "0.01", + "description": "A clock showing quotes every hour", + "icon": "app.img", + "type": "clock", + "tags": "clock,shakespeare", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"quoteclock.app.js","url":"app.js"}, + {"name":"quoteclock.img","url":"app-icon.js","evaluate":true} + ] + } \ No newline at end of file From f41fcb8881d39d99dd625491773915c67b7767e8 Mon Sep 17 00:00:00 2001 From: pglez82 Date: Thu, 4 Jan 2024 19:20:43 +0100 Subject: [PATCH 15/70] adding app icon --- apps/quoteclock/app.png | Bin 0 -> 18335 bytes apps/quoteclock/metadata.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 apps/quoteclock/app.png diff --git a/apps/quoteclock/app.png b/apps/quoteclock/app.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb2241988f5b67ec8ff2e2a21e940e89d2eb33f GIT binary patch literal 18335 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 literal 0 HcmV?d00001 diff --git a/apps/quoteclock/metadata.json b/apps/quoteclock/metadata.json index 21220f173..0102f010e 100644 --- a/apps/quoteclock/metadata.json +++ b/apps/quoteclock/metadata.json @@ -3,7 +3,7 @@ "name": "Quote Clock", "version": "0.01", "description": "A clock showing quotes every hour", - "icon": "app.img", + "icon": "app.png", "type": "clock", "tags": "clock,shakespeare", "supports": ["BANGLEJS2"], From 2f5dd2b4b8e9078b96e1aea35126d30b92b30db6 Mon Sep 17 00:00:00 2001 From: pglez82 Date: Thu, 4 Jan 2024 19:24:26 +0100 Subject: [PATCH 16/70] adding changelog --- apps/intervals/ChangeLog | 2 ++ apps/intervals/metadata.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 apps/intervals/ChangeLog diff --git a/apps/intervals/ChangeLog b/apps/intervals/ChangeLog new file mode 100644 index 000000000..761350e07 --- /dev/null +++ b/apps/intervals/ChangeLog @@ -0,0 +1,2 @@ +0.01: First Release +0.02: Changing resolution to seconds instead of 5 seconds \ No newline at end of file diff --git a/apps/intervals/metadata.json b/apps/intervals/metadata.json index 32c18ae70..542fb1846 100644 --- a/apps/intervals/metadata.json +++ b/apps/intervals/metadata.json @@ -2,7 +2,7 @@ "id": "intervals", "name": "Intervals App", "shortName": "Intervals", - "version": "0.01", + "version": "0.02", "description": "Intervals for training. It is possible to configure work time and rest time and number of sets.", "icon": "intervals.png", "tags": "", From 7ab3fe2f37de68b802dcab074b3e00e4cfa1b45c Mon Sep 17 00:00:00 2001 From: Paul Spenke Date: Fri, 5 Jan 2024 12:15:31 +0100 Subject: [PATCH 17/70] Add new Line Clock application This commit introduces the Line Clock application. The Line Clock is a readable analog clock that is customizable via the theme configuration. It includes a JavaScript logic file, an app icon, a metadata JSON file, a README, and a ChangeLog file. This also includes the MIT license file. --- apps/line_clock/ChangeLog | 1 + apps/line_clock/LICENSE | 21 +++ apps/line_clock/README.md | 11 ++ apps/line_clock/app-icon.js | 1 + apps/line_clock/app-icon.png | Bin 0 -> 3511 bytes apps/line_clock/app-screenshot.png | Bin 0 -> 2653 bytes apps/line_clock/app.js | 273 +++++++++++++++++++++++++++++ apps/line_clock/metadata.json | 17 ++ 8 files changed, 324 insertions(+) create mode 100644 apps/line_clock/ChangeLog create mode 100644 apps/line_clock/LICENSE create mode 100644 apps/line_clock/README.md create mode 100644 apps/line_clock/app-icon.js create mode 100644 apps/line_clock/app-icon.png create mode 100644 apps/line_clock/app-screenshot.png create mode 100644 apps/line_clock/app.js create mode 100644 apps/line_clock/metadata.json diff --git a/apps/line_clock/ChangeLog b/apps/line_clock/ChangeLog new file mode 100644 index 000000000..0f7cf828d --- /dev/null +++ b/apps/line_clock/ChangeLog @@ -0,0 +1 @@ +0.1 init app diff --git a/apps/line_clock/LICENSE b/apps/line_clock/LICENSE new file mode 100644 index 000000000..404cbc7a0 --- /dev/null +++ b/apps/line_clock/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Paul Spenke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/apps/line_clock/README.md b/apps/line_clock/README.md new file mode 100644 index 000000000..5789acbbc --- /dev/null +++ b/apps/line_clock/README.md @@ -0,0 +1,11 @@ +# Line Clock + +This app displays a simple, different looking, analog clock. It considers the +currently configured "theme" (and may therefore look different than shown in +the screenshot on your watch depending on which theme you prefer). + +![](app-screenshot.png) + +## License + +[MIT License](LICENSE) diff --git a/apps/line_clock/app-icon.js b/apps/line_clock/app-icon.js new file mode 100644 index 000000000..eaaf719b4 --- /dev/null +++ b/apps/line_clock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgYMJh/4AgUD+AeKgIRDj/+n41O/4RQABcfIJYAEKZgAkL4U/8ARNBwIRP/+AGx6YBPSH/4ASPh/A/hfDAAZAHg/8gP/LguSoARHEwIRFiVJkDCFjgRHgEJkg4CcwQjIAAMEHAUDCoIRB46kIHAkH//xLIw4I8eAnCNKHAYAO/xxEABg4ByASPHAkBKAbUE/5xGhP//wRFv4RDOIYIB//ACQr1FHAIRJAA0TCAP/ZwIALgYRJVowRCj/4BIkBLIgABgRHC/KqFaI4RC5MkJBlPR4UECJizJJwoAKCKImVQAwAJv0HL5S6CbwIjLCKMAn4RDh0/LMKMhWaYAKA=")) diff --git a/apps/line_clock/app-icon.png b/apps/line_clock/app-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2753538122d298ede31a6526c23ac519dee6d1e7 GIT binary patch literal 3511 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 literal 0 HcmV?d00001 diff --git a/apps/line_clock/app.js b/apps/line_clock/app.js new file mode 100644 index 000000000..0596b865e --- /dev/null +++ b/apps/line_clock/app.js @@ -0,0 +1,273 @@ +const handWidth = 6; +const hourRadius = 4; +const hourWidth = 8; +const hourLength = 40; +const hourSLength = 20; +const radius = 220; +const lineOffset = 115; +const hourOffset = 32; +const numberOffset = 85; +const numberSize = 22; + +let settings = { + showLock: true +}; + +let gWidth = g.getWidth(), gCenterX = gWidth/2; +let gHeight = g.getHeight(), gCenterY = gHeight/2; + +let currentTime = new Date(); +let currentHour = currentTime.getHours(); +let currentMinute = currentTime.getMinutes(); + +let drawTimeout; + +function imgLock() { + return { + width : 16, height : 16, bpp : 1, + transparent : 0, + buffer : E.toArrayBuffer(atob("A8AH4A5wDDAYGBgYP/w//D/8Pnw+fD58Pnw//D/8P/w=")) + }; +} + +/** + * Retrieves the angle of the hour hand for the current time. + * + * @returns {number} The angle of the hour hand in degrees. + */ +function getHourHandAngle() { + let hourHandAngle = 30 * currentHour; + hourHandAngle += 0.5 * currentMinute; + return hourHandAngle; +} + +let hourAngle = getHourHandAngle(); + +/** + * Converts degrees to radians. + * + * @param {number} degrees - The degrees to be converted to radians. + * @return {number} - The equivalent value in radians. + */ +function degreesToRadians(degrees) { + return degrees * (Math.PI / 180); +} + +/** + * Rotates an array of points around a given angle and radius. + * + * @param {Array} points - The array of points to be rotated. + * @param {number} angle - The angle in degrees to rotate the points. + * @param {number} rad - The radius to offset the rotation. + * @returns {Array} - The array of rotated points. + */ +function rotatePoints(points, angle, rad) { + const ang = degreesToRadians(angle); + const hAng = degreesToRadians(hourAngle); + const rotatedPoints = []; + points.map(function(point) { + return { + x: point.x * Math.cos(ang) - point.y * Math.sin(ang), + y: point.x * Math.sin(ang) + point.y * Math.cos(ang) + }; + }).forEach(function(point) { + rotatedPoints.push(point.x + gCenterX - (rad * Math.sin(hAng))); + rotatedPoints.push(point.y + gCenterY + (rad * Math.cos(hAng))); + }); + return rotatedPoints; +} + +/** + * Draws a hand on the canvas. + * + * @function drawHand + * + * @returns {void} + */ +function drawHand() { + g.setColor(0xF800); + const halfWidth = handWidth / 2; + + const points = [{ + x: -halfWidth, + y: -gHeight + }, { + x: halfWidth, + y: -gHeight + }, { + x: halfWidth, + y: gHeight + }, { + x: -halfWidth, + y: gHeight + }]; + + g.fillPolyAA(rotatePoints(points, hourAngle, 0)); +} + +/** + * Retrieves the hour coordinates for a given small flag. + * @param {boolean} small - Determines if the flag is small. + * @returns {Array} - An array of hour coordinates. + */ +function getHourCoordinates(small) { + const dist = small ? (hourSLength - hourLength) : 0; + const halfWidth = hourWidth / 2; + const gh = gHeight + lineOffset; + return [{ + x: -halfWidth, + y: -gh - dist + }, { + x: halfWidth, + y: -gh - dist + }, { + x: halfWidth, + y: -gh + hourLength + }, { + x: -halfWidth, + y: -gh + hourLength + }]; +} + +/** + * Assign the given time to the hour dot on the clock face. + * + * @param {number} a - The time value to assign to the hour dot. + * @return {void} + */ +function hourDot(a) { + const h = gHeight + lineOffset; + const rotatedPoints = rotatePoints( + [{ + x: 0, + y: -h + hourLength - (hourRadius / 2) + }], a, radius + ); + g.fillCircle(rotatedPoints[0], rotatedPoints[1], hourRadius); +} + +/** + * Convert an hour into a number and display it on the clock face. + * + * @param {number} a - The hour to be converted (between 0 and 360 degrees). + */ +function hourNumber(a) { + const h = gHeight + lineOffset; + const rotatedPoints = rotatePoints( + [{ + x: 0, + y: -h + hourLength + hourOffset + }], a, radius + ); + g.drawString(String(a / 30), rotatedPoints[0], rotatedPoints[1]); +} + +/** + * Draws a number on the display. + * + * @param {number} n - The number to be drawn. + * @return {void} + */ +function drawNumber(n) { + const h = gHeight + lineOffset; + const halfWidth = handWidth / 2; + const rotatedPoints = rotatePoints( + [{ + x: 0, + y: -h + hourLength + numberOffset + }], hourAngle, radius + ); + g.setColor(0xF800); + g.fillCircle(rotatedPoints[0], rotatedPoints[1], numberSize+ halfWidth); + g.setColor(g.theme.bg); + g.fillCircle(rotatedPoints[0], rotatedPoints[1], numberSize - halfWidth); + g.setColor(g.theme.fg); + g.setFont("Vector:"+numberSize); + g.drawString(String(n), rotatedPoints[0], rotatedPoints[1]); +} + +const hourPoints = getHourCoordinates(false); +const hourSPoints = getHourCoordinates(true); + +/** + * Draws an hour on a clock face. + * + * @param {number} h - The hour to be drawn on the clock face. + * @return {undefined} + */ +function drawHour(h) { + if (h === 0) { h= 12; } + if (h === 13) { h= 1; } + g.setColor(g.theme.fg); + g.setFont("Vector:32"); + const a = h * 30; + g.fillPolyAA(rotatePoints(hourPoints, a, radius)); + g.fillPolyAA(rotatePoints(hourSPoints, a + 15, radius)); + hourNumber(a); + hourDot(a + 5); + hourDot(a + 10); + hourDot(a + 20); + hourDot(a + 25); +} + +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + +function lockListenerBw(isLocked) { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + draw(); +} +Bangle.on('lock', lockListenerBw); + +Bangle.setUI({ + mode : "clock", + remove : function() { + Bangle.removeListener('lock', lockListenerBw); + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +/** + * Draws a clock on the canvas using the current time. + * + * @return {undefined} + */ +function draw() { + queueDraw(); + currentTime = new Date(); + currentHour = currentTime.getHours(); + if (currentHour > 12) { + currentHour -= 12; + } + currentMinute = currentTime.getMinutes(); + + hourAngle = getHourHandAngle(); + + g.clear(); + g.setFontAlign(0, 0); + + g.setColor(g.theme.bg); + g.fillRect(0, 0, gWidth, gHeight); + + if(settings.showLock && Bangle.isLocked()){ + g.setColor(g.theme.fg); + g.drawImage(imgLock(), gWidth-16, 2); + } + + drawHour(currentHour); + drawHour(currentHour-1); + drawHour(currentHour+1); + + + drawHand(); + drawNumber(currentMinute); +} + +draw(); diff --git a/apps/line_clock/metadata.json b/apps/line_clock/metadata.json new file mode 100644 index 000000000..a2aad5f58 --- /dev/null +++ b/apps/line_clock/metadata.json @@ -0,0 +1,17 @@ +{ "id": "line_clock", + "name": "Line Clock", + "shortName":"Line Clock", + "version":"0.1", + "description": "a readable analog clock", + "icon": "app-icon.png", + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "allow_emulator": true, + "screenshots": [{"url":"app-screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"line_clock.app.js","url":"app.js"}, + {"name":"line_clock.img","url":"app-icon.js","evaluate":true} + ] +} From a54d37dbfc33f8f088bc75146c89289243c69b67 Mon Sep 17 00:00:00 2001 From: Paul Spenke Date: Fri, 5 Jan 2024 19:15:55 +0100 Subject: [PATCH 18/70] Implement showMinute setting in Line Clock app The commit adds 'showMinute' configuration in both app and settings file for the Line Clock app, allowing users to choose whether to display the minute. An additional property within the settings JSON file is now read at application start to adjust behavior accordingly. --- apps/line_clock/app.js | 17 ++++++++++++--- apps/line_clock/metadata.json | 6 ++++-- apps/line_clock/settings.js | 39 +++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 apps/line_clock/settings.js diff --git a/apps/line_clock/app.js b/apps/line_clock/app.js index 0596b865e..4d243b736 100644 --- a/apps/line_clock/app.js +++ b/apps/line_clock/app.js @@ -9,10 +9,18 @@ const hourOffset = 32; const numberOffset = 85; const numberSize = 22; +const SETTINGS_FILE = "line_clock.setting.json"; + let settings = { - showLock: true + showLock: true, + showMinute: true, }; +let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; +for (const key in saved_settings) { + settings[key] = saved_settings[key]; +} + let gWidth = g.getWidth(), gCenterX = gWidth/2; let gHeight = g.getHeight(), gCenterY = gHeight/2; @@ -218,7 +226,7 @@ function queueDraw() { }, 60000 - (Date.now() % 60000)); } -function lockListenerBw(isLocked) { +function lockListenerBw() { if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; draw(); @@ -267,7 +275,10 @@ function draw() { drawHand(); - drawNumber(currentMinute); + + if(settings.showMinute){ + drawNumber(currentMinute); + } } draw(); diff --git a/apps/line_clock/metadata.json b/apps/line_clock/metadata.json index a2aad5f58..01393efdf 100644 --- a/apps/line_clock/metadata.json +++ b/apps/line_clock/metadata.json @@ -12,6 +12,8 @@ "readme": "README.md", "storage": [ {"name":"line_clock.app.js","url":"app.js"}, - {"name":"line_clock.img","url":"app-icon.js","evaluate":true} - ] + {"name":"line_clock.img","url":"app-icon.js","evaluate":true}, + {"name":"line_clock.settings.js","url":"settings.js"} + ], + "data":[{"name":"line_clock.setting.json"}] } diff --git a/apps/line_clock/settings.js b/apps/line_clock/settings.js new file mode 100644 index 000000000..5a793ed0f --- /dev/null +++ b/apps/line_clock/settings.js @@ -0,0 +1,39 @@ +(function(back) { + const SETTINGS_FILE = "line_clock.setting.json"; + + // initialize with default settings... + const storage = require('Storage') + let settings = { + showLock: true, + showMinute: true, + }; + let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; + for (const key in saved_settings) { + settings[key] = saved_settings[key] + } + + function save() { + storage.write(SETTINGS_FILE, settings) + } + + E.showMenu({ + '': { 'title': 'Line Clock' }, + '< Back': back, + 'Show Lock': { + value: settings.showLock, + format: () => (settings.showLock ? 'Yes' : 'No'), + onchange: () => { + settings.showLock = !settings.showLock; + save(); + }, + }, + 'Show Minute': { + value: settings.showMinute, + format: () => (settings.showMinute ? 'Yes' : 'No'), + onchange: () => { + settings.showMinute = !settings.showMinute; + save(); + }, + } + }); + }) From e62d3fd2198965e29add4407559da7ada84ea0a1 Mon Sep 17 00:00:00 2001 From: Paul Spenke Date: Sat, 6 Jan 2024 15:00:52 +0100 Subject: [PATCH 19/70] Add Storage requirement to Line Clock app --- apps/line_clock/app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/line_clock/app.js b/apps/line_clock/app.js index 4d243b736..67ad742f5 100644 --- a/apps/line_clock/app.js +++ b/apps/line_clock/app.js @@ -9,6 +9,8 @@ const hourOffset = 32; const numberOffset = 85; const numberSize = 22; +const storage = require('Storage'); + const SETTINGS_FILE = "line_clock.setting.json"; let settings = { From 651d08de5316a282deb8c319c4b474b21de4507c Mon Sep 17 00:00:00 2001 From: Paul Spenke Date: Sat, 6 Jan 2024 15:06:25 +0100 Subject: [PATCH 20/70] Update changelog --- apps/line_clock/ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/line_clock/ChangeLog b/apps/line_clock/ChangeLog index 0f7cf828d..504dc0efe 100644 --- a/apps/line_clock/ChangeLog +++ b/apps/line_clock/ChangeLog @@ -1 +1 @@ -0.1 init app +0.1: init app From 6e3066d444912275999cc747cd76ba8e128e6950 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 8 Jan 2024 21:45:50 +0000 Subject: [PATCH 21/70] sanitycheck: warn on boolean-formatters --- bin/sanitycheck.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js index f1e795156..945bb8bff 100755 --- a/bin/sanitycheck.js +++ b/bin/sanitycheck.js @@ -256,9 +256,17 @@ apps.forEach((app,appIdx) => { if (a>=0 && b>=0 && a !d.name || !d.name.endsWith(".json")))) { - WARN(`App ${app.id} has a setting file but no corresponding data entry (add \`"data":[{"name":"${app.id}.settings.json"}]\`)`, {file:appDirRelative+file.url}); + // if settings + if (/\.settings?\.js$/.test(file.name)) { + // suggest adding to datafiles + if (!app.data || app.data.every(d => !d.name || !d.name.endsWith(".json"))) { + WARN(`App ${app.id} has a setting file but no corresponding data entry (add \`"data":[{"name":"${app.id}.settings.json"}]\`)`, {file:appDirRelative+file.url}); + } + // check for manual boolean formatter + const m = fileContents.match(/format: *\(\) *=>.*["']Yes["']/); + if (m) { + WARN(`Settings for ${app.id} has a boolean formatter - this is handled automatically, the line can be removed`, {file:appDirRelative+file.url, line: fileContents.substr(0, m.index).split("\n").length}); + } } } for (const key in file) { From b5b65406c000881afdd56f88992d0578375bcc71 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 8 Jan 2024 21:46:46 +0000 Subject: [PATCH 22/70] Update apps to remove default boolean formatters --- apps/90sclk/settings.js | 1 - apps/banglexercise/settings.js | 1 - apps/bwclk/settings.js | 2 -- apps/bwclklite/settings.js | 2 -- apps/circlesclock/settings.js | 5 +---- apps/clicompleteclk/settings.js | 4 ---- apps/fuzzyw/fuzzyw.settings.js | 1 - apps/health/settings.js | 1 - apps/lcars/lcars.settings.js | 4 ---- apps/limelight/limelight.settings.js | 5 +---- apps/linuxclock/settings.js | 2 -- apps/mosaic/mosaic.settings.js | 1 - apps/pebble/pebble.settings.js | 3 +-- apps/slopeclockpp/settings.js | 8 -------- apps/weather/settings.js | 1 - apps/widbgjs/settings.js | 1 - apps/widpedom/settings.js | 3 --- 17 files changed, 3 insertions(+), 42 deletions(-) diff --git a/apps/90sclk/settings.js b/apps/90sclk/settings.js index 8f97cd317..74241d603 100644 --- a/apps/90sclk/settings.js +++ b/apps/90sclk/settings.js @@ -21,7 +21,6 @@ '< Back': back, 'Full Screen': { value: settings.fullscreen, - format: () => (settings.fullscreen ? 'Yes' : 'No'), onchange: () => { settings.fullscreen = !settings.fullscreen; save(); diff --git a/apps/banglexercise/settings.js b/apps/banglexercise/settings.js index 3208c6eca..0b52acd72 100644 --- a/apps/banglexercise/settings.js +++ b/apps/banglexercise/settings.js @@ -11,7 +11,6 @@ '< Back': back, 'Buzz': { value: "buzz" in settings ? settings.buzz : false, - format: () => (settings.buzz ? 'Yes' : 'No'), onchange: () => { settings.buzz = !settings.buzz; save('buzz', settings.buzz); diff --git a/apps/bwclk/settings.js b/apps/bwclk/settings.js index 116253fda..8bcf0ae0f 100644 --- a/apps/bwclk/settings.js +++ b/apps/bwclk/settings.js @@ -32,7 +32,6 @@ }, 'Show Lock': { value: settings.showLock, - format: () => (settings.showLock ? 'Yes' : 'No'), onchange: () => { settings.showLock = !settings.showLock; save(); @@ -40,7 +39,6 @@ }, 'Hide Colon': { value: settings.hideColon, - format: () => (settings.hideColon ? 'Yes' : 'No'), onchange: () => { settings.hideColon = !settings.hideColon; save(); diff --git a/apps/bwclklite/settings.js b/apps/bwclklite/settings.js index 2d3916a3d..4c59198c6 100644 --- a/apps/bwclklite/settings.js +++ b/apps/bwclklite/settings.js @@ -32,7 +32,6 @@ }, 'Show Lock': { value: settings.showLock, - format: () => (settings.showLock ? 'Yes' : 'No'), onchange: () => { settings.showLock = !settings.showLock; save(); @@ -40,7 +39,6 @@ }, 'Hide Colon': { value: settings.hideColon, - format: () => (settings.hideColon ? 'Yes' : 'No'), onchange: () => { settings.hideColon = !settings.hideColon; save(); diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index 63a2b0f93..ae090c1d7 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -30,7 +30,6 @@ }, /*LANG*/'show widgets': { value: !!settings.showWidgets, - format: () => (settings.showWidgets ? 'Yes' : 'No'), onchange: x => save('showWidgets', x), }, /*LANG*/'update interval': { @@ -45,7 +44,6 @@ }, /*LANG*/'show big weather': { value: !!settings.showBigWeather, - format: () => (settings.showBigWeather ? 'Yes' : 'No'), onchange: x => save('showBigWeather', x), }, /*LANG*/'colorize icons': ()=>showCircleMenus() @@ -87,8 +85,7 @@ const colorizeIconKey = circleName + "colorizeIcon"; menu[/*LANG*/'circle ' + circleId] = { value: settings[colorizeIconKey] || false, - format: () => (settings[colorizeIconKey]? /*LANG*/'Yes': /*LANG*/'No'), - onchange: x => save(colorizeIconKey, x), + onchange: x => save(colorizeIconKey, x), }; } E.showMenu(menu); diff --git a/apps/clicompleteclk/settings.js b/apps/clicompleteclk/settings.js index 2df20ed3e..0213ead6e 100644 --- a/apps/clicompleteclk/settings.js +++ b/apps/clicompleteclk/settings.js @@ -9,7 +9,6 @@ '': { 'title': 'CLI complete clk' }, 'Show battery': { value: "battery" in settings ? settings.battery : false, - format: () => (settings.battery ? 'Yes' : 'No'), onchange: () => { settings.battery = !settings.battery; save('battery', settings.battery); @@ -27,7 +26,6 @@ }, 'Show weather': { value: "weather" in settings ? settings.weather : false, - format: () => (settings.weather ? 'Yes' : 'No'), onchange: () => { settings.weather = !settings.weather; save('weather', settings.weather); @@ -35,7 +33,6 @@ }, 'Show steps': { value: "steps" in settings ? settings.steps : false, - format: () => (settings.steps ? 'Yes' : 'No'), onchange: () => { settings.steps = !settings.steps; save('steps', settings.steps); @@ -43,7 +40,6 @@ }, 'Show heartrate': { value: "heartrate" in settings ? settings.heartrate : false, - format: () => (settings.heartrate ? 'Yes' : 'No'), onchange: () => { settings.heartrate = !settings.heartrate; save('heartrate', settings.heartrate); diff --git a/apps/fuzzyw/fuzzyw.settings.js b/apps/fuzzyw/fuzzyw.settings.js index 8feb30bfb..535f91d67 100644 --- a/apps/fuzzyw/fuzzyw.settings.js +++ b/apps/fuzzyw/fuzzyw.settings.js @@ -23,7 +23,6 @@ '< Back': back, 'Show Widgets': { value: settings.showWidgets, - format: () => (settings.showWidgets ? 'Yes' : 'No'), onchange: () => { settings.showWidgets = !settings.showWidgets; save(); diff --git a/apps/health/settings.js b/apps/health/settings.js index 88c8061c6..4d6e1a067 100644 --- a/apps/health/settings.js +++ b/apps/health/settings.js @@ -43,7 +43,6 @@ /*LANG*/"Step Goal Notification": { value: "stepGoalNotification" in settings ? settings.stepGoalNotification : false, - format: () => (settings.stepGoalNotification ? 'Yes' : 'No'), onchange: () => { settings.stepGoalNotification = !settings.stepGoalNotification; setSettings(); diff --git a/apps/lcars/lcars.settings.js b/apps/lcars/lcars.settings.js index db583741f..2ce3e4fc9 100644 --- a/apps/lcars/lcars.settings.js +++ b/apps/lcars/lcars.settings.js @@ -76,7 +76,6 @@ var bg_code = [ }, 'Full Screen': { value: settings.fullscreen, - format: () => (settings.fullscreen ? 'Yes' : 'No'), onchange: () => { settings.fullscreen = !settings.fullscreen; save(); @@ -120,7 +119,6 @@ var bg_code = [ }, 'Disable alarm functionality': { value: settings.disableAlarms, - format: () => (settings.disableAlarms ? 'Yes' : 'No'), onchange: () => { settings.disableAlarms = !settings.disableAlarms; save(); @@ -128,7 +126,6 @@ var bg_code = [ }, 'Disable data pages functionality': { value: settings.disableData, - format: () => (settings.disableData ? 'Yes' : 'No'), onchange: () => { settings.disableData = !settings.disableData; save(); @@ -136,7 +133,6 @@ var bg_code = [ }, 'Random colors on open': { value: settings.randomColors, - format: () => (settings.randomColors ? 'Yes' : 'No'), onchange: () => { settings.randomColors = !settings.randomColors; save(); diff --git a/apps/limelight/limelight.settings.js b/apps/limelight/limelight.settings.js index aacea2f86..fa1d857d2 100644 --- a/apps/limelight/limelight.settings.js +++ b/apps/limelight/limelight.settings.js @@ -27,13 +27,12 @@ } var font_options = ["Limelight","GochiHand","Grenadier","Monoton"]; - + E.showMenu({ '': { 'title': 'Limelight Clock' }, '< Back': back, 'Full Screen': { value: s.fullscreen, - format: () => (s.fullscreen ? 'Yes' : 'No'), onchange: () => { s.fullscreen = !s.fullscreen; save(); @@ -50,7 +49,6 @@ }, 'Vector Font': { value: s.vector, - format: () => (s.vector ? 'Yes' : 'No'), onchange: () => { s.vector = !s.vector; save(); @@ -68,7 +66,6 @@ }, 'Second Hand': { value: s.secondhand, - format: () => (s.secondhand ? 'Yes' : 'No'), onchange: () => { s.secondhand = !s.secondhand; save(); diff --git a/apps/linuxclock/settings.js b/apps/linuxclock/settings.js index 116253fda..8bcf0ae0f 100644 --- a/apps/linuxclock/settings.js +++ b/apps/linuxclock/settings.js @@ -32,7 +32,6 @@ }, 'Show Lock': { value: settings.showLock, - format: () => (settings.showLock ? 'Yes' : 'No'), onchange: () => { settings.showLock = !settings.showLock; save(); @@ -40,7 +39,6 @@ }, 'Hide Colon': { value: settings.hideColon, - format: () => (settings.hideColon ? 'Yes' : 'No'), onchange: () => { settings.hideColon = !settings.hideColon; save(); diff --git a/apps/mosaic/mosaic.settings.js b/apps/mosaic/mosaic.settings.js index dcf725b84..ee80cf950 100644 --- a/apps/mosaic/mosaic.settings.js +++ b/apps/mosaic/mosaic.settings.js @@ -25,7 +25,6 @@ '< Back': back, 'Show Widgets': { value: settings.showWidgets, - format: () => (settings.showWidgets ? 'Yes' : 'No'), onchange: () => { settings.showWidgets = !settings.showWidgets; save(); diff --git a/apps/pebble/pebble.settings.js b/apps/pebble/pebble.settings.js index f1c065db4..83032270a 100644 --- a/apps/pebble/pebble.settings.js +++ b/apps/pebble/pebble.settings.js @@ -23,7 +23,7 @@ var color_options = ['Green','Orange','Cyan','Purple','Red','Blue']; var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f']; var theme_options = ['System', 'Light', 'Dark']; - + E.showMenu({ '': { 'title': 'Pebble Clock' }, /*LANG*/'< Back': back, @@ -48,7 +48,6 @@ }, /*LANG*/'Show Lock': { value: settings.showlock, - format: () => (settings.showlock ? /*LANG*/'Yes' : /*LANG*/'No'), onchange: () => { settings.showlock = !settings.showlock; save(); diff --git a/apps/slopeclockpp/settings.js b/apps/slopeclockpp/settings.js index 3c0e0a6e9..2c2d2c463 100644 --- a/apps/slopeclockpp/settings.js +++ b/apps/slopeclockpp/settings.js @@ -21,42 +21,34 @@ }, /*LANG*/'Red': { value: !!settings.colorRed, - format: () => (settings.colorRed ? 'Yes' : 'No'), onchange: x => save('colorRed', x), }, /*LANG*/'Green': { value: !!settings.colorGreen, - format: () => (settings.colorGreen ? 'Yes' : 'No'), onchange: x => save('colorGreen', x), }, /*LANG*/'Blue': { value: !!settings.colorBlue, - format: () => (settings.colorBlue ? 'Yes' : 'No'), onchange: x => save('colorBlue', x), }, /*LANG*/'Magenta': { value: !!settings.colorMagenta, - format: () => (settings.colorMagenta ? 'Yes' : 'No'), onchange: x => save('colorMagenta', x), }, /*LANG*/'Cyan': { value: !!settings.colorCyan, - format: () => (settings.colorCyan ? 'Yes' : 'No'), onchange: x => save('colorCyan', x), }, /*LANG*/'Yellow': { value: !!settings.colorYellow, - format: () => (settings.colorYellow ? 'Yes' : 'No'), onchange: x => save('colorYellow', x), }, /*LANG*/'Black': { value: !!settings.colorBlack, - format: () => (settings.colorBlack ? 'Yes' : 'No'), onchange: x => save('colorBlack', x), }, /*LANG*/'White': { value: !!settings.colorWhite, - format: () => (settings.colorWhite ? 'Yes' : 'No'), onchange: x => save('colorWhite', x), } }; diff --git a/apps/weather/settings.js b/apps/weather/settings.js index 7e2c043b9..7e0bb24c8 100644 --- a/apps/weather/settings.js +++ b/apps/weather/settings.js @@ -21,7 +21,6 @@ }, 'Hide Widget': { value: "hide" in settings ? settings.hide : false, - format: () => (settings.hide ? 'Yes' : 'No'), onchange: () => { settings.hide = !settings.hide save('hide', settings.hide); diff --git a/apps/widbgjs/settings.js b/apps/widbgjs/settings.js index e480c8cf9..c599183dc 100644 --- a/apps/widbgjs/settings.js +++ b/apps/widbgjs/settings.js @@ -43,7 +43,6 @@ }, 'Hide Widget': { value: s.hide, - format: () => (s.hide ? 'Yes' : 'No'), onchange: () => { s.hide = !s.hide; save(); diff --git a/apps/widpedom/settings.js b/apps/widpedom/settings.js index 4455ce7d7..24e8c9b35 100644 --- a/apps/widpedom/settings.js +++ b/apps/widpedom/settings.js @@ -37,7 +37,6 @@ }, 'Show Progress': { value: s.progress, - format: () => (s.progress ? 'Yes' : 'No'), onchange: () => { s.progress = !s.progress save(); @@ -45,7 +44,6 @@ }, 'Large Digits': { value: s.large, - format: () => (s.large ? 'Yes' : 'No'), onchange: () => { s.large = !s.large save(); @@ -53,7 +51,6 @@ }, 'Hide Widget': { value: s.hide, - format: () => (s.hide ? 'Yes' : 'No'), onchange: () => { s.hide = !s.hide save(); From 8b7ee2390d63f14c2f8a568889226327f6d181be Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sat, 13 Jan 2024 09:11:54 +0100 Subject: [PATCH 23/70] calendar: Load holidays before events Previously events were loaded and as result painted first and a following draw on a holiday would overwrite the event indicator --- apps/calendar/ChangeLog | 1 + apps/calendar/calendar.js | 29 +++++++++++++++++------------ apps/calendar/metadata.json | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/apps/calendar/ChangeLog b/apps/calendar/ChangeLog index 6edb54f65..bd8e6117b 100644 --- a/apps/calendar/ChangeLog +++ b/apps/calendar/ChangeLog @@ -17,3 +17,4 @@ 0.15: Edit holidays on device in settings 0.16: Add menu to fast open settings to edit holidays Display Widgets in menus +0.17: Load holidays before events so the latter is not overpainted diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js index 7477775ca..555a068ba 100644 --- a/apps/calendar/calendar.js +++ b/apps/calendar/calendar.js @@ -43,24 +43,24 @@ const dowLbls = function() { }(); const loadEvents = () => { + // add holidays & other events + events = (require("Storage").readJSON("calendar.days.json",1) || []).map(d => { + const date = new Date(d.date); + const o = {date: date, msg: d.name, type: d.type}; + if (d.repeat) { + o.repeat = d.repeat; + } + return o; + }); // all alarms that run on a specific date - events = (require("Storage").readJSON("sched.json",1) || []).filter(a => a.on && a.date).map(a => { + events = events.concat((require("Storage").readJSON("sched.json",1) || []).filter(a => a.on && a.date).map(a => { const date = new Date(a.date); const time = timeutils.decodeTime(a.t); date.setHours(time.h); date.setMinutes(time.m); date.setSeconds(time.s); return {date: date, msg: a.msg, type: "e"}; - }); - // add holidays & other events - (require("Storage").readJSON("calendar.days.json",1) || []).forEach(d => { - const date = new Date(d.date); - const o = {date: date, msg: d.name, type: d.type}; - if (d.repeat) { - o.repeat = d.repeat; - } - events.push(o); - }); + })); }; const loadSettings = () => { @@ -141,6 +141,7 @@ const calcDays = (month, monthMaxDayMap, dowNorm) => { }; const drawCalendar = function(date) { + d1 = new Date(); g.setBgColor(bgColor); g.clearRect(0, 0, maxX, maxY); g.setBgColor(bgColorMonth); @@ -281,7 +282,11 @@ const showMenu = function() { }, /*LANG*/"Exit": () => load(), /*LANG*/"Settings": () => { - const appSettings = eval(require('Storage').read('calendar.settings.js')); + const appSettings = () => eval(require('Storage').read('calendar.settings.js'))(() => { + loadSettings(); + loadEvents(); + showMenu(); + }); appSettings(() => { loadSettings(); loadEvents(); diff --git a/apps/calendar/metadata.json b/apps/calendar/metadata.json index e263efe35..895f8f7aa 100644 --- a/apps/calendar/metadata.json +++ b/apps/calendar/metadata.json @@ -1,7 +1,7 @@ { "id": "calendar", "name": "Calendar", - "version": "0.16", + "version": "0.17", "description": "Monthly calendar, displays holidays uploaded from the web interface and scheduled events.", "icon": "calendar.png", "screenshots": [{"url":"screenshot_calendar.png"}], From a75338efae5b0c5610c42d9c243ae7c9b91538eb Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sat, 13 Jan 2024 11:50:22 +0000 Subject: [PATCH 24/70] fwupdate: don't reset the watch after upload Closes #3153. Trace: ``` apps/fwupdate/custom.html:423: calls `sendCustomizedApp()` core/lib/customize.js:63: send customised app core/js/index.js:340: handle `"app"` message core/js/index.js:356: call `uploadApp()` core/js/comms.js:166: call `showUploadFinished()` core/js/comms.js:24: reset watch (if autoReload is set) ``` --- apps/fwupdate/custom.html | 2 +- core | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/fwupdate/custom.html b/apps/fwupdate/custom.html index 066d658be..3feab6591 100644 --- a/apps/fwupdate/custom.html +++ b/apps/fwupdate/custom.html @@ -426,7 +426,7 @@ function handleUpload() { storage:[ {name:"RAM", content:hexJS}, ] - }); + }, true); } document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false); diff --git a/core b/core index c97b7851f..3bbca52f3 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit c97b7851f50cfff4e898c2264a337a17085ce463 +Subproject commit 3bbca52f36237e92265749ee86069468460488a1 From ae25c46690a00b6170af724a0bf319a82699bfee Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 15 Jan 2024 07:06:34 +0100 Subject: [PATCH 25/70] calendar: remove debug statement --- apps/calendar/calendar.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js index 555a068ba..a6e78aab7 100644 --- a/apps/calendar/calendar.js +++ b/apps/calendar/calendar.js @@ -141,7 +141,6 @@ const calcDays = (month, monthMaxDayMap, dowNorm) => { }; const drawCalendar = function(date) { - d1 = new Date(); g.setBgColor(bgColor); g.clearRect(0, 0, maxX, maxY); g.setBgColor(bgColorMonth); From ef7695bc769ec2a0b8e89d09f733ed031f9eabe8 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 15 Jan 2024 07:15:30 +0100 Subject: [PATCH 26/70] calendar: remove duplicate settings call --- apps/calendar/calendar.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js index a6e78aab7..f9fd43de8 100644 --- a/apps/calendar/calendar.js +++ b/apps/calendar/calendar.js @@ -280,18 +280,12 @@ const showMenu = function() { setUI(); }, /*LANG*/"Exit": () => load(), - /*LANG*/"Settings": () => { - const appSettings = () => eval(require('Storage').read('calendar.settings.js'))(() => { + /*LANG*/"Settings": () => + eval(require('Storage').read('calendar.settings.js'))(() => { loadSettings(); loadEvents(); showMenu(); - }); - appSettings(() => { - loadSettings(); - loadEvents(); - showMenu(); - }); - }, + }), }; if (require("Storage").read("alarm.app.js")) { menu[/*LANG*/"Launch Alarms"] = () => { From 99bb9a2d2e8fc5767952b968e18d5eff0aa7e147 Mon Sep 17 00:00:00 2001 From: Flaparoo <629229+flaparoo@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:25:25 +0800 Subject: [PATCH 27/70] dragboard: fix touch region for letters --- apps/dragboard/lib.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dragboard/lib.js b/apps/dragboard/lib.js index 78ef11bd4..2e40f3a77 100644 --- a/apps/dragboard/lib.js +++ b/apps/dragboard/lib.js @@ -107,7 +107,7 @@ exports.input = function(options) { "ram"; // ABCDEFGHIJKLMNOPQRSTUVWXYZ // Choose character by draging along red rectangle at bottom of screen - if (event.y >= ( (R.y+R.h) - 12 )) { + if (event.y >= ( (R.y+R.h) - 26 )) { // Translate x-position to character if (event.x < ABCPADDING) { abcHL = 0; } else if (event.x >= 176-ABCPADDING) { abcHL = 25; } @@ -139,7 +139,7 @@ exports.input = function(options) { // 12345678901234567890 // Choose number or puctuation by draging on green rectangle - else if ((event.y < ( (R.y+R.h) - 12 )) && (event.y > ( (R.y+R.h) - 52 ))) { + else if ((event.y < ( (R.y+R.h) - 26 )) && (event.y > ( (R.y+R.h) - 52 ))) { // Translate x-position to character if (event.x < NUMPADDING) { numHL = 0; } else if (event.x > 176-NUMPADDING) { numHL = NUM.length-1; } From 33c5bd1b7986e9e7a4dfbbd4bded453f445334fe Mon Sep 17 00:00:00 2001 From: Flaparoo <629229+flaparoo@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:27:03 +0800 Subject: [PATCH 28/70] dragboard: update version for fix --- apps/dragboard/ChangeLog | 1 + apps/dragboard/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/dragboard/ChangeLog b/apps/dragboard/ChangeLog index 68cd82cfa..77cc63c98 100644 --- a/apps/dragboard/ChangeLog +++ b/apps/dragboard/ChangeLog @@ -8,3 +8,4 @@ 0.08: Catch and discard swipe events on fw2v19 and up (as well as some cutting edge 2v18 ones), allowing compatability with the Back Swipe app. 0.09: Fix colors settings, where color was stored as string instead of the expected int. +0.10: Fix touch region for letters diff --git a/apps/dragboard/metadata.json b/apps/dragboard/metadata.json index 090c37a01..c4596d7bd 100644 --- a/apps/dragboard/metadata.json +++ b/apps/dragboard/metadata.json @@ -1,6 +1,6 @@ { "id": "dragboard", "name": "Dragboard", - "version":"0.09", + "version":"0.10", "description": "A library for text input via swiping keyboard", "icon": "app.png", "type":"textinput", From 8f7528699fb48e627960d1d9666a90ae08df391f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 15 Jan 2024 12:06:19 +0000 Subject: [PATCH 29/70] Add better testing for 2v20 bootloader --- apps/fwupdate/custom.html | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/fwupdate/custom.html b/apps/fwupdate/custom.html index 066d658be..b1a356d4e 100644 --- a/apps/fwupdate/custom.html +++ b/apps/fwupdate/custom.html @@ -88,23 +88,28 @@ function onInit(device) { document.getElementById("fw-unknown").style = "display:none"; document.getElementById("fw-ok").style = ""; } - Puck.eval("E.CRC32(E.memoryArea(0xF7000,0x7000))", crc => { - console.log("DFU CRC = "+crc); - var version = `unknown (CRC ${crc})`; + Puck.eval("[E.CRC32(E.memoryArea(0xF7000,0x6000)),E.CRC32(E.memoryArea(0xF7000,0x7000))]", crcs => { + console.log("DFU CRC (6 pages) = "+crcs[0]); + console.log("DFU CRC (7 pages) = "+crcs[1]); + var version = `unknown (CRC ${crcs[1]})`; var ok = true; - if (crc==1339551013) { version = "2v10.219"; ok = false; } - if (crc==1207580954) { version = "2v10.236"; ok = false; } - if (crc==3435933210) version = "2v11.52"; - if (crc==46757280) version = "2v11.58"; - if (crc==3508163280 || crc==1418074094) version = "2v12"; - if (crc==4056371285) version = "2v13"; - if (crc==1038322422) version = "2v14"; - if (crc==2560806221) version = "2v15"; - if (crc==2886730689) version = "2v16"; - if (crc==156320890) version = "2v17"; - if (crc==4012421318) version = "2v18"; - if (crc==1856454048) version = "2v19"; - if (crc==2893810756 || crc==1273571156) version = "2v20"; + if (crcs[0] == 1787004733) { // check 6 page CRC - the 7th page isn't used in 2v20 + version = "2v20"; + } else { // for other versions all 7 pages are used, check those + var crc = crcs[2]; + if (crc==1339551013) { version = "2v10.219"; ok = false; } + if (crc==1207580954) { version = "2v10.236"; ok = false; } + if (crc==3435933210) version = "2v11.52"; + if (crc==46757280) version = "2v11.58"; + if (crc==3508163280 || crc==1418074094) version = "2v12"; + if (crc==4056371285) version = "2v13"; + if (crc==1038322422) version = "2v14"; + if (crc==2560806221) version = "2v15"; + if (crc==2886730689) version = "2v16"; + if (crc==156320890) version = "2v17"; + if (crc==4012421318) version = "2v18"; + if (crc==1856454048) version = "2v19"; + } if (!ok) { version += `(⚠ update required)`; } From fd0c46608af5221d8246630aab30013a30bf64b6 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 15 Jan 2024 20:53:10 +0000 Subject: [PATCH 30/70] fwupdate: pass { noFinish: true } for app --- apps/fwupdate/custom.html | 2 +- core | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/fwupdate/custom.html b/apps/fwupdate/custom.html index 3feab6591..a5f96d98d 100644 --- a/apps/fwupdate/custom.html +++ b/apps/fwupdate/custom.html @@ -426,7 +426,7 @@ function handleUpload() { storage:[ {name:"RAM", content:hexJS}, ] - }, true); + }, { noFinish: true }); } document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false); diff --git a/core b/core index 3bbca52f3..41e586e3b 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 3bbca52f36237e92265749ee86069468460488a1 +Subproject commit 41e586e3bb1025253ebdaa27a6be32e19619837f From 9175d5cc0e903c90a7ce394407be1ddeabf28e69 Mon Sep 17 00:00:00 2001 From: shansou504 <123512155+shansou504@users.noreply.github.com> Date: Wed, 17 Jan 2024 13:26:28 -0500 Subject: [PATCH 31/70] added settings for fullscreen --- apps/binaryclk/ChangeLog | 1 + apps/binaryclk/app.js | 24 +++++++++++++++++------- apps/binaryclk/metadata.json | 4 +++- apps/binaryclk/settings.js | 20 ++++++++++++++++++++ 4 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 apps/binaryclk/settings.js diff --git a/apps/binaryclk/ChangeLog b/apps/binaryclk/ChangeLog index a78cbe479..7b6810faa 100644 --- a/apps/binaryclk/ChangeLog +++ b/apps/binaryclk/ChangeLog @@ -1,2 +1,3 @@ 0.01: Added app 0.02: Removed unneeded squares +0.03: Added settings with fullscreen option diff --git a/apps/binaryclk/app.js b/apps/binaryclk/app.js index 8b030ccff..988d2ec3f 100644 --- a/apps/binaryclk/app.js +++ b/apps/binaryclk/app.js @@ -1,3 +1,7 @@ +var settings = require('Storage').readJSON("binaryclk.json", true) || {}; +var full = settings.fullscreen || false; +delete settings; + function draw() { var dt = new Date(); var h = dt.getHours(), m = dt.getMinutes(); @@ -11,9 +15,14 @@ function draw() { g.clearRect(Bangle.appRect); let i = 0; + if (full) { + const gap = 12; + const mgn = 0; + } else { + const gap = 8; + const mgn = 20; + } const sq = 29; - const gap = 8; - const mgn = 20; const pos = sq + gap; for (let r = 3; r >= 0; r--) { @@ -26,14 +35,15 @@ function draw() { } i++; } - g.clearRect(mgn/2 + gap, mgn + gap, mgn/2 + gap + sq, mgn + 2 * gap + 2 * sq); - g.clearRect(mgn/2 + 3 * gap + 2 * sq, mgn + gap, mgn/2 + 3 * gap + 3 * sq, mgn + gap + sq); + g.clearRect(mgn/2 + gap, mgn + gap, mgn/2 + gap + sq, mgn + 2 * gap + 2 * sq); + g.clearRect(mgn/2 + 3 * gap + 2 * sq, mgn + gap, mgn/2 + 3 * gap + 3 * sq, mgn + gap + sq); } - g.clear(); draw(); var secondInterval = setInterval(draw, 60000); Bangle.setUI("clock"); -Bangle.loadWidgets(); -Bangle.drawWidgets(); +if (!full) { + Bangle.loadWidgets(); + Bangle.drawWidgets(); +} diff --git a/apps/binaryclk/metadata.json b/apps/binaryclk/metadata.json index a81a39b7a..37a8c8c0d 100644 --- a/apps/binaryclk/metadata.json +++ b/apps/binaryclk/metadata.json @@ -11,6 +11,8 @@ "allow_emulator": true, "storage": [ {"name":"binaryclk.app.js","url":"app.js"}, + {"name":"binaryclk.settings.js","url":"settings.js"}, {"name":"binaryclk.img","url":"app-icon.js","evaluate":true} - ] + ], + "data": [{"name":"binaryclk.json"}] } diff --git a/apps/binaryclk/settings.js b/apps/binaryclk/settings.js new file mode 100644 index 000000000..3ae758de2 --- /dev/null +++ b/apps/binaryclk/settings.js @@ -0,0 +1,20 @@ +(function(back) { + var FILE = "binaryclk.json"; + var settings = Object.assign({ + fullscreen: false, + }, require('Storage').readJSON(FILE, true) || {}); + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + E.showMenu({ + "" : { "title" : "Bin Clock" }, + "< Back" : () => back(), + 'Fullscreen': { + value: settings.fullscreen, + onchange: v => { + settings.fullscreen = !v; + writeSettings(); + } + }, + }); +}) From 3deef94948c21d09bfa84a91e59f86649111f708 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 00:31:10 +0000 Subject: [PATCH 32/70] build(deps): bump core from `41e586e` to `e6a65a8` Bumps [core](https://github.com/espruino/EspruinoAppLoaderCore) from `41e586e` to `e6a65a8`. - [Commits](https://github.com/espruino/EspruinoAppLoaderCore/compare/41e586e3bb1025253ebdaa27a6be32e19619837f...e6a65a8cb20a730f75bbbab549c602300e69e8c4) --- updated-dependencies: - dependency-name: core dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index 41e586e3b..e6a65a8cb 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 41e586e3bb1025253ebdaa27a6be32e19619837f +Subproject commit e6a65a8cb20a730f75bbbab549c602300e69e8c4 From c06612b4005254c8792dc1719e08743cf7e1373d Mon Sep 17 00:00:00 2001 From: shansou504 <123512155+shansou504@users.noreply.github.com> Date: Thu, 18 Jan 2024 09:34:29 -0500 Subject: [PATCH 33/70] add fullscreen option --- apps/binaryclk/app.js | 21 ++++++------ apps/binaryclk/binaryclk.app.js | 49 ++++++++++++++++++++++++++++ apps/binaryclk/binaryclk.info | 15 +++++++++ apps/binaryclk/binaryclk.settings.js | 20 ++++++++++++ apps/binaryclk/metadata.json | 2 +- apps/binaryclk/settings.js | 34 ++++++++++--------- 6 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 apps/binaryclk/binaryclk.app.js create mode 100644 apps/binaryclk/binaryclk.info create mode 100644 apps/binaryclk/binaryclk.settings.js diff --git a/apps/binaryclk/app.js b/apps/binaryclk/app.js index 988d2ec3f..94c906104 100644 --- a/apps/binaryclk/app.js +++ b/apps/binaryclk/app.js @@ -1,6 +1,6 @@ -var settings = require('Storage').readJSON("binaryclk.json", true) || {}; -var full = settings.fullscreen || false; -delete settings; +var settings = Object.assign({ + fullscreen: false, +}, require('Storage').readJSON("binaryclk.json", true) || {}); function draw() { var dt = new Date(); @@ -15,15 +15,14 @@ function draw() { g.clearRect(Bangle.appRect); let i = 0; - if (full) { - const gap = 12; - const mgn = 0; - } else { - const gap = 8; - const mgn = 20; + var gap = 8; + var mgn = 20; + if (settings.fullscreen) { + gap = 12; + mgn = 0; } const sq = 29; - const pos = sq + gap; + var pos = sq + gap; for (let r = 3; r >= 0; r--) { for (let c = 0; c < 4; c++) { @@ -43,7 +42,7 @@ g.clear(); draw(); var secondInterval = setInterval(draw, 60000); Bangle.setUI("clock"); -if (!full) { +if (!settings.fullscreen) { Bangle.loadWidgets(); Bangle.drawWidgets(); } diff --git a/apps/binaryclk/binaryclk.app.js b/apps/binaryclk/binaryclk.app.js new file mode 100644 index 000000000..02e6172f5 --- /dev/null +++ b/apps/binaryclk/binaryclk.app.js @@ -0,0 +1,49 @@ +var settings = Object.assign({ + fullscreen: false, +}, require('Storage').readJSON("binaryclk.json", true) || {}); +var full = settings.fullscreen; + +function draw() { + var dt = new Date(); + var h = dt.getHours(), m = dt.getMinutes(); + const t = []; + t[0] = Math.floor(h/10); + t[1] = Math.floor(h%10); + t[2] = Math.floor(m/10); + t[3] = Math.floor(m%10); + + g.reset(); + g.clearRect(Bangle.appRect); + + let i = 0; + var gap = 8; + var mgn = 20; + if (full) { + gap = 12; + mgn = 0; + } + const sq = 29; + const pos = sq + gap; + + for (let r = 3; r >= 0; r--) { + for (let c = 0; c < 4; c++) { + if (t[c] & Math.pow(2, r)) { + g.fillRect(mgn/2 + gap + c * pos, mgn + gap + i * pos, mgn/2 + gap + c * pos + sq, mgn + gap + i * pos + sq); + } else { + g.drawRect(mgn/2 + gap + c * pos, mgn + gap + i * pos, mgn/2 + gap + c * pos + sq, mgn + gap + i * pos + sq); + } + } + i++; + } + g.clearRect(mgn/2 + gap, mgn + gap, mgn/2 + gap + sq, mgn + 2 * gap + 2 * sq); + g.clearRect(mgn/2 + 3 * gap + 2 * sq, mgn + gap, mgn/2 + 3 * gap + 3 * sq, mgn + gap + sq); +} + +g.clear(); +draw(); +var secondInterval = setInterval(draw, 60000); +Bangle.setUI("clock"); +if (!full) { + Bangle.loadWidgets(); + Bangle.drawWidgets(); +} diff --git a/apps/binaryclk/binaryclk.info b/apps/binaryclk/binaryclk.info new file mode 100644 index 000000000..6dcdcb435 --- /dev/null +++ b/apps/binaryclk/binaryclk.info @@ -0,0 +1,15 @@ +require("Storage").write("binaryclk.info",{ + "id": "binaryclk", + "name": "Bin Clock", + "version": "0.03", + "description": "Clock face to show binary time in 24 hr format", + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"binaryclk.app.js","url":"app.js"}, + {"name":"binaryclk.settings.js","url":"settings.js"} + ], + "data": [{"name":"binaryclk.json"}] +}); diff --git a/apps/binaryclk/binaryclk.settings.js b/apps/binaryclk/binaryclk.settings.js new file mode 100644 index 000000000..3ae758de2 --- /dev/null +++ b/apps/binaryclk/binaryclk.settings.js @@ -0,0 +1,20 @@ +(function(back) { + var FILE = "binaryclk.json"; + var settings = Object.assign({ + fullscreen: false, + }, require('Storage').readJSON(FILE, true) || {}); + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + E.showMenu({ + "" : { "title" : "Bin Clock" }, + "< Back" : () => back(), + 'Fullscreen': { + value: settings.fullscreen, + onchange: v => { + settings.fullscreen = !v; + writeSettings(); + } + }, + }); +}) diff --git a/apps/binaryclk/metadata.json b/apps/binaryclk/metadata.json index 37a8c8c0d..b4ddc6544 100644 --- a/apps/binaryclk/metadata.json +++ b/apps/binaryclk/metadata.json @@ -1,7 +1,7 @@ { "id": "binaryclk", "name": "Bin Clock", - "version": "0.02", + "version": "0.03", "description": "Clock face to show binary time in 24 hr format", "icon": "app-icon.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/binaryclk/settings.js b/apps/binaryclk/settings.js index 3ae758de2..0ef30e19d 100644 --- a/apps/binaryclk/settings.js +++ b/apps/binaryclk/settings.js @@ -1,20 +1,22 @@ (function(back) { - var FILE = "binaryclk.json"; - var settings = Object.assign({ + var FILE = "binaryclk.json"; + var settings = Object.assign({ fullscreen: false, }, require('Storage').readJSON(FILE, true) || {}); - function writeSettings() { - require('Storage').writeJSON(FILE, settings); - } - E.showMenu({ - "" : { "title" : "Bin Clock" }, - "< Back" : () => back(), - 'Fullscreen': { - value: settings.fullscreen, - onchange: v => { - settings.fullscreen = !v; - writeSettings(); - } - }, - }); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + E.showMenu({ + "" : { "title" : "Bin Clock" }, + "< Back" : () => back(), + 'Fullscreen': { + value: settings.fullscreen, + onchange: v => { + settings.fullscreen = v; + writeSettings(); + } + }, + }); }) From 657c8898cc7760ccde7418901f85bfcbfb0a1954 Mon Sep 17 00:00:00 2001 From: shansou504 <123512155+shansou504@users.noreply.github.com> Date: Thu, 18 Jan 2024 09:45:14 -0500 Subject: [PATCH 34/70] remove extra files --- apps/binaryclk/binaryclk.app.js | 49 ---------------------------- apps/binaryclk/binaryclk.info | 15 --------- apps/binaryclk/binaryclk.settings.js | 20 ------------ 3 files changed, 84 deletions(-) delete mode 100644 apps/binaryclk/binaryclk.app.js delete mode 100644 apps/binaryclk/binaryclk.info delete mode 100644 apps/binaryclk/binaryclk.settings.js diff --git a/apps/binaryclk/binaryclk.app.js b/apps/binaryclk/binaryclk.app.js deleted file mode 100644 index 02e6172f5..000000000 --- a/apps/binaryclk/binaryclk.app.js +++ /dev/null @@ -1,49 +0,0 @@ -var settings = Object.assign({ - fullscreen: false, -}, require('Storage').readJSON("binaryclk.json", true) || {}); -var full = settings.fullscreen; - -function draw() { - var dt = new Date(); - var h = dt.getHours(), m = dt.getMinutes(); - const t = []; - t[0] = Math.floor(h/10); - t[1] = Math.floor(h%10); - t[2] = Math.floor(m/10); - t[3] = Math.floor(m%10); - - g.reset(); - g.clearRect(Bangle.appRect); - - let i = 0; - var gap = 8; - var mgn = 20; - if (full) { - gap = 12; - mgn = 0; - } - const sq = 29; - const pos = sq + gap; - - for (let r = 3; r >= 0; r--) { - for (let c = 0; c < 4; c++) { - if (t[c] & Math.pow(2, r)) { - g.fillRect(mgn/2 + gap + c * pos, mgn + gap + i * pos, mgn/2 + gap + c * pos + sq, mgn + gap + i * pos + sq); - } else { - g.drawRect(mgn/2 + gap + c * pos, mgn + gap + i * pos, mgn/2 + gap + c * pos + sq, mgn + gap + i * pos + sq); - } - } - i++; - } - g.clearRect(mgn/2 + gap, mgn + gap, mgn/2 + gap + sq, mgn + 2 * gap + 2 * sq); - g.clearRect(mgn/2 + 3 * gap + 2 * sq, mgn + gap, mgn/2 + 3 * gap + 3 * sq, mgn + gap + sq); -} - -g.clear(); -draw(); -var secondInterval = setInterval(draw, 60000); -Bangle.setUI("clock"); -if (!full) { - Bangle.loadWidgets(); - Bangle.drawWidgets(); -} diff --git a/apps/binaryclk/binaryclk.info b/apps/binaryclk/binaryclk.info deleted file mode 100644 index 6dcdcb435..000000000 --- a/apps/binaryclk/binaryclk.info +++ /dev/null @@ -1,15 +0,0 @@ -require("Storage").write("binaryclk.info",{ - "id": "binaryclk", - "name": "Bin Clock", - "version": "0.03", - "description": "Clock face to show binary time in 24 hr format", - "type": "clock", - "tags": "clock", - "supports": ["BANGLEJS2"], - "allow_emulator": true, - "storage": [ - {"name":"binaryclk.app.js","url":"app.js"}, - {"name":"binaryclk.settings.js","url":"settings.js"} - ], - "data": [{"name":"binaryclk.json"}] -}); diff --git a/apps/binaryclk/binaryclk.settings.js b/apps/binaryclk/binaryclk.settings.js deleted file mode 100644 index 3ae758de2..000000000 --- a/apps/binaryclk/binaryclk.settings.js +++ /dev/null @@ -1,20 +0,0 @@ -(function(back) { - var FILE = "binaryclk.json"; - var settings = Object.assign({ - fullscreen: false, - }, require('Storage').readJSON(FILE, true) || {}); - function writeSettings() { - require('Storage').writeJSON(FILE, settings); - } - E.showMenu({ - "" : { "title" : "Bin Clock" }, - "< Back" : () => back(), - 'Fullscreen': { - value: settings.fullscreen, - onchange: v => { - settings.fullscreen = !v; - writeSettings(); - } - }, - }); -}) From 3f16edcaf83f90fef746c69bc36f2e1f41564fde Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Thu, 18 Jan 2024 19:09:54 +0100 Subject: [PATCH 35/70] forge: Compatibility with Fastload Utils ... by adding the string `"Bangle.loadWidget()";`. This way Fastload Utils thinks widgets would be present and so fastloads. But in the absence of Fastload Utils no widgets will be loaded, and so no overhead is added for the usual case. --- apps/forge/ChangeLog | 1 + apps/forge/forge.app.js | 2 ++ apps/forge/metadata.json | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/forge/ChangeLog b/apps/forge/ChangeLog index 263d4078d..0c651c90b 100644 --- a/apps/forge/ChangeLog +++ b/apps/forge/ChangeLog @@ -1 +1,2 @@ 0.01: attempt to import +0.02: Make it possible for Fastload Utils to fastload into this app. diff --git a/apps/forge/forge.app.js b/apps/forge/forge.app.js index b972e13bb..b179fb540 100644 --- a/apps/forge/forge.app.js +++ b/apps/forge/forge.app.js @@ -1,5 +1,7 @@ // App Forge +"Bangle.loadWidgets()"; // Facilitates fastloading to this app via Fastload Utils, while still not loading widgets on standard `load` calls. + st = require('Storage'); l = /^a\..*\.js$/; diff --git a/apps/forge/metadata.json b/apps/forge/metadata.json index 03671a647..6e13a4df3 100644 --- a/apps/forge/metadata.json +++ b/apps/forge/metadata.json @@ -1,6 +1,6 @@ { "id": "forge", "name": "App Forge", - "version":"0.01", + "version":"0.02", "description": "Easy way to run development versions of your apps", "icon": "app.png", "readme": "README.md", From fa88a3edc00776aa0f418bca2ac6d3cd17f4c2a5 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Thu, 18 Jan 2024 18:43:14 +0000 Subject: [PATCH 36/70] sanitycheck: improve boolean formatter regex check for "on", and make case-insensitive --- bin/sanitycheck.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js index 945bb8bff..4e6662e4a 100755 --- a/bin/sanitycheck.js +++ b/bin/sanitycheck.js @@ -263,7 +263,7 @@ apps.forEach((app,appIdx) => { WARN(`App ${app.id} has a setting file but no corresponding data entry (add \`"data":[{"name":"${app.id}.settings.json"}]\`)`, {file:appDirRelative+file.url}); } // check for manual boolean formatter - const m = fileContents.match(/format: *\(\) *=>.*["']Yes["']/); + const m = fileContents.match(/format: *\(\) *=>.*["'](yes|on)["']/i); if (m) { WARN(`Settings for ${app.id} has a boolean formatter - this is handled automatically, the line can be removed`, {file:appDirRelative+file.url, line: fileContents.substr(0, m.index).split("\n").length}); } From c21ea99724bcfab50d0b6b593f28c329ef59ad6c Mon Sep 17 00:00:00 2001 From: Paul Spenke Date: Sat, 20 Jan 2024 19:48:33 +0100 Subject: [PATCH 37/70] Refactor settings in line_clock app The `settings` object has been renamed to `initialSettings` for better clarity as these values represent the initial, default settings. Also, the code to remove the clock UI has been commented out, possibly signaling future work on applying the Fast Load system, but this needs to be confirmed. Lastly, calling settings to draw locking and minute indicators has been updated to call the renamed `initialSettings` object. --- apps/line_clock/app.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/line_clock/app.js b/apps/line_clock/app.js index 67ad742f5..eadc46fad 100644 --- a/apps/line_clock/app.js +++ b/apps/line_clock/app.js @@ -13,14 +13,14 @@ const storage = require('Storage'); const SETTINGS_FILE = "line_clock.setting.json"; -let settings = { +let initialSettings = { showLock: true, showMinute: true, }; -let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; +let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || initialSettings; for (const key in saved_settings) { - settings[key] = saved_settings[key]; + initialSettings[key] = saved_settings[key]; } let gWidth = g.getWidth(), gCenterX = gWidth/2; @@ -237,11 +237,12 @@ Bangle.on('lock', lockListenerBw); Bangle.setUI({ mode : "clock", - remove : function() { - Bangle.removeListener('lock', lockListenerBw); - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; - } + // TODO implement https://www.espruino.com/Bangle.js+Fast+Load + // remove : function() { + // Bangle.removeListener('lock', lockListenerBw); + // if (drawTimeout) clearTimeout(drawTimeout); + // drawTimeout = undefined; + // } }); /** @@ -266,7 +267,7 @@ function draw() { g.setColor(g.theme.bg); g.fillRect(0, 0, gWidth, gHeight); - if(settings.showLock && Bangle.isLocked()){ + if(initialSettings.showLock && Bangle.isLocked()){ g.setColor(g.theme.fg); g.drawImage(imgLock(), gWidth-16, 2); } @@ -278,7 +279,7 @@ function draw() { drawHand(); - if(settings.showMinute){ + if(initialSettings.showMinute){ drawNumber(currentMinute); } } From ef94eab3633458920c8a1f2f7e8ed639b8e93cb6 Mon Sep 17 00:00:00 2001 From: thyttan <97237430+thyttan@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:58:30 +0100 Subject: [PATCH 38/70] Update settings.js Delete custom formatting for booleans in settings, falling back on the default checkboxes. --- apps/line_clock/settings.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/line_clock/settings.js b/apps/line_clock/settings.js index 5a793ed0f..5da04e959 100644 --- a/apps/line_clock/settings.js +++ b/apps/line_clock/settings.js @@ -21,7 +21,6 @@ '< Back': back, 'Show Lock': { value: settings.showLock, - format: () => (settings.showLock ? 'Yes' : 'No'), onchange: () => { settings.showLock = !settings.showLock; save(); @@ -29,7 +28,6 @@ }, 'Show Minute': { value: settings.showMinute, - format: () => (settings.showMinute ? 'Yes' : 'No'), onchange: () => { settings.showMinute = !settings.showMinute; save(); From 7b8762b5ce26f475effe5deb33b49e6c007afee4 Mon Sep 17 00:00:00 2001 From: Hairo Date: Sun, 21 Jan 2024 18:43:16 -0400 Subject: [PATCH 39/70] [Rebble] add fullscreen option --- apps/rebble/ChangeLog | 1 + apps/rebble/metadata.json | 2 +- apps/rebble/rebble.app.js | 133 +++++++++++++++++++++++++-------- apps/rebble/rebble.settings.js | 12 ++- 4 files changed, 114 insertions(+), 34 deletions(-) diff --git a/apps/rebble/ChangeLog b/apps/rebble/ChangeLog index 78ba0c5da..bde6fda82 100644 --- a/apps/rebble/ChangeLog +++ b/apps/rebble/ChangeLog @@ -14,3 +14,4 @@ 0.14: cleanup code and fix fastload issue 0.15: fix draw before widget hide 0.16: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps +0.17: Add fullscreen option (on by default) to show widgets, adjust sidebar 1 and 2 when fullscreen is off diff --git a/apps/rebble/metadata.json b/apps/rebble/metadata.json index 6242236c8..449185e87 100644 --- a/apps/rebble/metadata.json +++ b/apps/rebble/metadata.json @@ -2,7 +2,7 @@ "id": "rebble", "name": "Rebble Clock", "shortName": "Rebble", - "version": "0.16", + "version": "0.17", "description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion", "readme": "README.md", "icon": "rebble.png", diff --git a/apps/rebble/rebble.app.js b/apps/rebble/rebble.app.js index 445c30125..06c344b2c 100644 --- a/apps/rebble/rebble.app.js +++ b/apps/rebble/rebble.app.js @@ -40,7 +40,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) { } let loadSettings=function() { - settings = {'bg': '#0f0', 'color': 'Green', 'autoCycle': true,'sideTap':0}; + settings = {'bg': '#0f0', 'color': 'Green', 'autoCycle': true, 'fullScreen': true, 'sideTap':0}; //sideTap 0 = on | 1 = sidebar1... let tmp = require('Storage').readJSON(SETTINGS_FILE, 1) || settings; @@ -118,32 +118,60 @@ Graphics.prototype.setFontKdamThmor = function(scale) { if (drawCount % 60 == 0) updateSunRiseSunSet(location.lat, location.lon); - + g.reset(); - g.setColor(g.theme.bg); - g.fillRect(0, 0, w2, h); - g.setColor(settings.bg); - g.fillRect(w2, 0, w, h); + + if (settings.fullScreen) { + g.setColor(g.theme.bg); + g.fillRect(0, 0, w2, h); + g.setColor(settings.bg); + g.fillRect(w2, 0, w, h); - // time - g.setColor(g.theme.fg); - g.setFontKdamThmor(); - g.setFontAlign(0, -1); - g.drawString(hh, w2/2, 10 + 0); - g.drawString(mm, w2/2, 10 + h/2); + // time + g.setColor(g.theme.fg); + g.setFontKdamThmor(); + g.setFontAlign(0, -1); + g.drawString(hh, w2/2, 10 + 0); + g.drawString(mm, w2/2, 10 + h/2); - switch(sideBar) { - case 0: - drawSideBar1(); - break; - case 1: - drawSideBar2(); - break; - case 2: - drawSideBar3(); - break; + switch(sideBar) { + case 0: + drawSideBar1(); + break; + case 1: + drawSideBar2(); + break; + case 2: + drawSideBar3(); + break; + } + } else { + g.setColor(g.theme.bg); + g.fillRect(0, 24, 113, 176); + g.setColor(settings.bg); + g.fillRect(113, 24, 176, 176); + + // time + g.setColor(g.theme.fg); + g.setFontKdamThmor(); + g.setFontAlign(0, -1); + g.drawString(hh, 57, 24); + g.drawString(mm, 57, 100); + + switch(sideBar) { + case 0: + drawSideBar1Alt(); + break; + case 1: + drawSideBar2Alt(); + break; + case 2: + drawSideBar3(); + break; + } } + drawCount++; queueDraw(); } @@ -164,6 +192,16 @@ Graphics.prototype.setFontKdamThmor = function(scale) { drawDateAndCalendar(w3, h/2, dy, dd, mm); } + + let drawSideBar1Alt=function() { + let date = new Date(); + let dy= require("date_utils").dow(date.getDay(),1).toUpperCase(); + let dd= date.getDate(); + let mm= require("date_utils").month(date.getMonth()+1,1).toUpperCase(); + let yy = date.getFullYear(); + + drawDateAndCalendarAlt(145, 46, dy, dd, mm, yy); + } let drawSideBar2=function() { drawBattery(w2 + (w-w2-wb)/2, h/10, wb, 17); @@ -178,6 +216,14 @@ Graphics.prototype.setFontKdamThmor = function(scale) { setSmallFont(); g.setFontAlign(0, -1); g.drawString(formatSteps(), w3, 7*h/8); + } + + let drawSideBar2Alt=function() { + // steps + g.drawImage(boot_img, 113, 59, { scale: 1 }); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(formatSteps(), 145, 122); } // sunrise, sunset times @@ -212,6 +258,28 @@ Graphics.prototype.setFontKdamThmor = function(scale) { g.setFontAlign(0, -1); g.drawString(mm.toUpperCase(), x, y + 70); } + + let drawDateAndCalendarAlt=function(x, y, dy, dd, mm, yy) { + // day + setTextColor(); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(dy.toUpperCase(), x, y); + + drawCalendar(x - 18, y + 28, 35, 3, dd); + + // month + setTextColor(); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(mm.toUpperCase(), x, y + 70); + + // year + setTextColor(); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(yy, x, y + 94); + } // at x,y width:wi thicknes:th let drawCalendar=function(x,y,wi,th,str) { @@ -311,7 +379,10 @@ Graphics.prototype.setFontKdamThmor = function(scale) { if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; delete Graphics.prototype.setFontKdamThmor; - Bangle.removeListener('charging',chargingListener); + + if (settings.fullScreen) + Bangle.removeListener('charging',chargingListener); + } } let main=function(){ @@ -341,17 +412,17 @@ Graphics.prototype.setFontKdamThmor = function(scale) { }); } - - - Bangle.on('charging',chargingListener); - - Bangle.loadWidgets(); - require("widget_utils").hide(); + + if (settings.fullScreen) { + Bangle.on('charging',chargingListener); + require("widget_utils").hide(); + } else { + Bangle.drawWidgets(); + } + draw(); - } - main(); } diff --git a/apps/rebble/rebble.settings.js b/apps/rebble/rebble.settings.js index 46252d156..444faa75f 100644 --- a/apps/rebble/rebble.settings.js +++ b/apps/rebble/rebble.settings.js @@ -2,7 +2,7 @@ const SETTINGS_FILE = "rebble.json"; // initialize with default settings... - let localSettings = {'bg': '#0f0', 'color': 'Green', 'autoCycle': true, 'sideTap':0}; + let localSettings = {'bg': '#0f0', 'color': 'Green', 'autoCycle': true, 'fullScreen': true, 'sideTap':0}; //sideTap 0 = on| 1= sideBar1 | 2 = ... // ...and overwrite them with any saved values @@ -38,6 +38,14 @@ save(); }, }, + 'Fullscreen': { + value: localSettings.fullScreen, + onchange: (v) => { + localSettings.fullScreen = v; + save(); + showMenu(); + } + } 'Auto Cycle': { value: localSettings.autoCycle, onchange: (v) => { @@ -74,4 +82,4 @@ } showMenu(); -}) \ No newline at end of file +}) From 3bce4cfb75eaf174450631b3c9a98fd381fde87e Mon Sep 17 00:00:00 2001 From: Hairo Date: Sun, 21 Jan 2024 18:53:59 -0400 Subject: [PATCH 40/70] [Rebble] Fix build issues --- apps/rebble/rebble.app.js | 2 +- apps/rebble/rebble.settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/rebble/rebble.app.js b/apps/rebble/rebble.app.js index 06c344b2c..bc18d997d 100644 --- a/apps/rebble/rebble.app.js +++ b/apps/rebble/rebble.app.js @@ -380,7 +380,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) { drawTimeout = undefined; delete Graphics.prototype.setFontKdamThmor; - if (settings.fullScreen) + if (settings.fullScreen) { Bangle.removeListener('charging',chargingListener); } } diff --git a/apps/rebble/rebble.settings.js b/apps/rebble/rebble.settings.js index 444faa75f..0048955c6 100644 --- a/apps/rebble/rebble.settings.js +++ b/apps/rebble/rebble.settings.js @@ -45,7 +45,7 @@ save(); showMenu(); } - } + }, 'Auto Cycle': { value: localSettings.autoCycle, onchange: (v) => { From 02cb97ee5d8a9ed71eab3c4354f4d5f923eb0858 Mon Sep 17 00:00:00 2001 From: Hairo Date: Sun, 21 Jan 2024 19:12:20 -0400 Subject: [PATCH 41/70] [Rebble] Update metadata and readme --- apps/rebble/README.md | 10 +++++++++- apps/rebble/metadata.json | 2 +- apps/rebble/screenshot_rebble_w1.png | Bin 0 -> 3474 bytes apps/rebble/screenshot_rebble_w2.png | Bin 0 -> 3397 bytes apps/rebble/screenshot_rebble_w3.png | Bin 0 -> 4111 bytes 5 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 apps/rebble/screenshot_rebble_w1.png create mode 100644 apps/rebble/screenshot_rebble_w2.png create mode 100644 apps/rebble/screenshot_rebble_w3.png diff --git a/apps/rebble/README.md b/apps/rebble/README.md index 0ecb51d7a..e55978dc4 100644 --- a/apps/rebble/README.md +++ b/apps/rebble/README.md @@ -10,14 +10,22 @@ * Tap top or bottom right to instantly cycle to the next sidebar * Uses pedometer widget to get latest step count * Dependant apps are installed when Rebble installs -* Uses the whole screen, widgets are made invisible but still run in the background +* When in fullscreen widgets are made invisible but still run in the background * The icon is James Dean - 'Rebel Without a Cause' +## Fullscreen + ![](screenshot_rebble.png) ![](screenshot_rebble2.png) ![](screenshot_rebble3.png) ![](screenshot_rebble4.png) +## With widgets + +![](screenshot_rebble_w1.png) +![](screenshot_rebble_w2.png) +![](screenshot_rebble_w3.png) + ## Future Enhancements * Support for Weather Icons in the Steps Sidebar diff --git a/apps/rebble/metadata.json b/apps/rebble/metadata.json index 449185e87..56fe014ff 100644 --- a/apps/rebble/metadata.json +++ b/apps/rebble/metadata.json @@ -7,7 +7,7 @@ "readme": "README.md", "icon": "rebble.png", "dependencies": {"mylocation":"app"}, - "screenshots": [{"url":"screenshot_rebble.png"}], + "screenshots": [{"url":"screenshot_rebble.png"}, {"url":"screenshot_rebble2.png"}, {"url":"screenshot_rebble3.png"}, {"url":"screenshot_rebble4.png"}, {"url":"screenshot_rebble_w1.png"}, {"url":"screenshot_rebble_w2.png"}, {"url":"screenshot_rebble_w3.png"}], "type": "clock", "tags": "clock", "supports": ["BANGLEJS2"], diff --git a/apps/rebble/screenshot_rebble_w1.png b/apps/rebble/screenshot_rebble_w1.png new file mode 100644 index 0000000000000000000000000000000000000000..0057e862bbdfe839499f2745e35b0d1e9166f49c GIT binary patch literal 3474 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 S?R5lu5#VI+Zr5N-y!;;l86+0~ literal 0 HcmV?d00001 diff --git a/apps/rebble/screenshot_rebble_w3.png b/apps/rebble/screenshot_rebble_w3.png new file mode 100644 index 0000000000000000000000000000000000000000..5754e43a498a7b68f1a96b28f9f2bb77bf59ea57 GIT binary patch literal 4111 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 N002ovPDHLkV1mT*#Wnx{ literal 0 HcmV?d00001 From 1a1b79d41bbf657f244c16f55d47982153bdfe6c Mon Sep 17 00:00:00 2001 From: Anonymous941 Date: Sun, 21 Jan 2024 19:24:31 -0500 Subject: [PATCH 42/70] tetris: major overhaul Added score, levels, bugfixes and misc, inspired by NES tetris --- apps/tetris/ChangeLog | 1 + apps/tetris/metadata.json | 2 +- apps/tetris/tetris.app.js | 255 +++++++++++++++++++++++--------------- 3 files changed, 154 insertions(+), 104 deletions(-) diff --git a/apps/tetris/ChangeLog b/apps/tetris/ChangeLog index 86661f1b6..158cccc5b 100644 --- a/apps/tetris/ChangeLog +++ b/apps/tetris/ChangeLog @@ -2,3 +2,4 @@ 0.02: Better controls, implement game over. 0.03: Implement mode and level selection screens. 0.04: Bring back old controls as "swipe" in menu, exit with button press +0.10: Major overhaul: added score, levels, bugfixes and misc, inspired by NES tetris diff --git a/apps/tetris/metadata.json b/apps/tetris/metadata.json index ba73918d5..8b73ffb43 100644 --- a/apps/tetris/metadata.json +++ b/apps/tetris/metadata.json @@ -1,7 +1,7 @@ { "id": "tetris", "name": "Tetris", "shortName":"Tetris", - "version":"0.04", + "version":"0.10", "description": "Tetris", "icon": "tetris.png", "readme": "README.md", diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index e4295732b..baad84f77 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -41,7 +41,7 @@ const oy = 8; 2 .. accelerometer. 12 lines record. 3 .. altimeter */ -var control = 0, level = 0; +var control = 0, level = 0, lines = 0, score = 0; var alt_start = -9999; /* For altimeter control */ /* 0 .. menu 1 .. game @@ -77,7 +77,9 @@ function drawBoundingBox() { for (i=0; i<4; ++i) g.drawRect(ox-i-1, oy-i-1, ox+10*8+i, oy+20*8+i); } -function drawTile (tile, n, x, y, qClear) { +function drawTile(tile, n, x, y, qClear) { + if (state != 1) // stops tile from being drawn on the game over screen + return; if (qClear) g.setColor(0); else g.setColor(tcols[n].r, tcols[n].g, tcols[n].b); for (i=0; i0) pf[py+y][px+x+1] = ctn+1; // check for full lines + let clearCount = 0; for (y=19; y>0; y--) { var qFull = true; for (x=1; x<11; ++x) qFull &= pf[y][x]>0; if (qFull) { - nlines++; - dropInterval -= 5; - Bangle.buzz(30); + clearCount++; for (ny=y; ny>0; ny--) pf[ny] = JSON.parse(JSON.stringify(pf[ny-1])); redrawPF(y); - g.setColor(0).fillRect(5, 30, 41, 80).setColor(1, 1, 1).drawString(nlines.toString(), 22, 50); } } + if (clearCount) { + lines += clearCount; + let effectiveLevel = Math.max(level, 1); + if (clearCount == 1) { // single + score += 100 * effectiveLevel; + Bangle.buzz(80, 0.5); + } + else if (clearCount == 2) { // double + score += 300 * effectiveLevel; + Bangle.buzz(80); + } + else if (clearCount == 3) { // triple + score += 500 * effectiveLevel; + Bangle.buzz(150); + } + else if (clearCount >= 4) { // tetris + score += 800 * effectiveLevel; + Bangle.buzz(300); + } + if (lines != 0 && lines % 10 == 0) { + level++; + calculateSpeed(); + } + redrawStats(); + } // spawn new tile px = 4; py = 0; ctn = ntn; @@ -149,18 +204,33 @@ function insertAndCheck() { function moveOk(t, dx, dy) { var ok = true; - for (y=0; y 0) ok = false; return ok; } +function pauseGame() { + print("Paused"); + state = 3; +} + +function resumeGame() { + print("Resumed"); + state = 1; +} + function gameStep() { if (state != 1) return; if (Date.now()-time > dropInterval) { // drop one step time = Date.now(); - if (moveOk(ct, 0, 1)) { + if (level >= 15 && moveOk(ct, 0, 2)) { + // at level 15, pieces drop twile as quickly + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + py += 2; + } + else if (moveOk(ct, 0, 1)) { drawTile(ct, ctn, ox+px*8, oy+py*8, true); py++; } @@ -181,16 +251,18 @@ function rotate() { } function move(x, y) { - if (moveOk(ct, x, y)) { + r = moveOk(ct, x, y); + if (r) { drawTile(ct, ctn, ox+px*8, oy+py*8, true); px += x; py += y; drawTile(ct, ctn, ox+px*8, oy+py*8, false); } + return r; } function linear(x) { - print("Linear: ", x); + //print("Linear: ", x); let now = px / 10; if (x < now-0.06) move(-1, 0); @@ -200,36 +272,16 @@ function linear(x) { function newGame() { E.showMenu(); - Bangle.setUI({mode : "custom", btn: () => load()}); - if (control == 4) { // Swipe - Bangle.on("touch", (e) => { - t = rotateTile(ct, 3); - if (moveOk(t, 0, 0)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); - ct = t; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); - } + Bangle.setUI(); + if (control == 2) { + Bangle.on("accel", (e) => { + if (state != 1) return; + if (control != 2) return; + print(e.x); + linear((0.2-e.x) * 2.5); }); - - Bangle.on("swipe", (x,y) => { - if (y<0) y = 0; - if (moveOk(ct, x, y)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); - px += x; - py += y; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); - } - }); - } else { // control != 4 - if (control == 2) { // Tilt - Bangle.on("accel", (e) => { - if (state != 1) return; - if (control != 2) return; - print(e.x); - linear((0.2-e.x) * 2.5); - }); - } - if (control == 3) { // Move + } + if (control == 3) { Bangle.setBarometerPower(true); Bangle.on("pressure", (e) => { if (state != 1) return; @@ -238,86 +290,83 @@ function newGame() { if (alt_start == -9999) alt_start = a; a = a - alt_start; - print(e.altitude, a); + //print(e.altitude, a); linear(a); }); - } - Bangle.on("drag", (e) => { - let h = 176/2; - if (state == 2) { - if (e.b) - selectGame(); - return; - } - if (!e.b) - return; - if (state == 0) return; - if (e.y < h) { - if (e.x < h) - rotate(); - else { - let i = 0; - for (i=0; i<10; i++) { - move(0, 1); - g.flip(); - } - } - } else { - if (control == 1) - linear((e.x - 20) / 156); - if (control != 0) - return; - if (e.x < h) - move(-1, 0); - else - move(1, 0); - } - }); } + Bangle.on("drag", (e) => { + let h = 176/2; + if (state == 2) { + if (e.b) + selectGame(); + return; + } + if (!e.b) + return; + if (state == 0) return; + if (e.y < h) { + if (e.x < h) + rotate(); + else { + while (move(0, 1)) { + score++; + g.flip(); + } + redrawStats(true); + } + } else { + if (control == 1) + linear((e.x - 20) / 156); + if (control != 0) + return; + if (e.x < h) + move(-1, 0); + else + move(1, 0); + } + }); + setWatch(() => { + if (state == 1) + pauseGame(); + else if (state == 3) + resumeGame(); + }, BTN1, {repeat: true}); initGame(); drawGame(); state = 1; - var step = 450 - 50*level; - if (control == 3) - step = step*2; - dropInterval = step; - var gi = setInterval(gameStep, 50); + calculateSpeed(); + var gi = setInterval(gameStep, 20); } function drawGame() { drawBoundingBox(); g.setColor(1, 1, 1).setFontAlign(0, 1, 0) - .setFont("6x15", 1).drawString("Lines", 22, 30) + .setFont("6x15", 1).drawString("Score", 22, 30) + .drawString("Level", 22, 80) + .drawString("Lines", 22, 130) .drawString("Next", 176-22, 30); showNext(ntn, ntr); - g.setColor(0).fillRect(5, 30, 41, 80) - .setColor(1, 1, 1).drawString(nlines.toString(), 22, 50); -} - -function selectLevel() { - print("Level selection menu"); - - var menu = {}; - menu["< Back"] = () => {selectGame();}; - menu[/*LANG*/"Level 1"] = () => { level = 0; selectGame(); }; - menu[/*LANG*/"Level 2"] = () => { level = 1; selectGame(); }; - menu[/*LANG*/"Level 3"] = () => { level = 2; selectGame(); }; - E.showMenu(menu); + redrawStats(); } function selectGame() { state = 0; print("Game selection menu"); - //for (let i = 0; i < 100000; i++) ; - + var menu = {}; - menu[/*LANG*/"Normal"] = () => { control = 0; newGame(); }; - menu[/*LANG*/"Drag"] = () => { control = 1; newGame(); }; - menu[/*LANG*/"Tilt"] = () => { control = 2; newGame(); }; - menu[/*LANG*/"Move"] = () => { control = 3; newGame(); }; - menu[/*LANG*/"Swipe"] = () => { control = 4; newGame(); }; - menu[/*LANG*/"Level"] = () => { selectLevel(); }; + menu["Normal"] = () => { control = 0; newGame(); }; + menu["Drag"] = () => { control = 1; newGame(); }; + menu["Tilt"] = () => { control = 2; newGame(); }; + menu["Pressure"] = () => { control = 3; newGame(); }; + level = 1; + menu["Level"] = { + value : 1, + min : 0, + max : 10, + wrap : true, + onchange : (l) => { level = l; } + }; E.showMenu(menu); } From 812a4cfaf469951d5ccedac7f2e8bdeb3a90e7cf Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 22 Jan 2024 14:39:04 +0000 Subject: [PATCH 43/70] Add VT100 termional app --- apps/terminal/ChangeLog | 1 + apps/terminal/app-icon.js | 1 + apps/terminal/app.js | 263 ++++++++++++++++++++++++++++++++++ apps/terminal/app.png | Bin 0 -> 393 bytes apps/terminal/metadata.json | 14 ++ apps/terminal/screenshot1.png | Bin 0 -> 2337 bytes apps/terminal/screenshot2.png | Bin 0 -> 3204 bytes 7 files changed, 279 insertions(+) create mode 100644 apps/terminal/ChangeLog create mode 100644 apps/terminal/app-icon.js create mode 100644 apps/terminal/app.js create mode 100644 apps/terminal/app.png create mode 100644 apps/terminal/metadata.json create mode 100644 apps/terminal/screenshot1.png create mode 100644 apps/terminal/screenshot2.png diff --git a/apps/terminal/ChangeLog b/apps/terminal/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/terminal/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/terminal/app-icon.js b/apps/terminal/app-icon.js new file mode 100644 index 000000000..1c35a67d2 --- /dev/null +++ b/apps/terminal/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4cA///w8h7fOm2V/8fjkf/Gt58jKf4AtiVJkmSARFICK3PlnyCJ1t03aEcBZjCBVJCP4R/CK9N8gRPpmUEaGSuRHP+1KCJ8m5AjgI4PyNZ+cAQIQCwgjKCgQRNdP4R/CL4PJAQIRWAH4ApA==")) \ No newline at end of file diff --git a/apps/terminal/app.js b/apps/terminal/app.js new file mode 100644 index 000000000..79a50c73f --- /dev/null +++ b/apps/terminal/app.js @@ -0,0 +1,263 @@ +/* Espruino VT100 JS REPL + + +TODO: Add option to connect to a remote BLE device + +*/ + +var settings = Object.assign({ + // default values + textSize: 1, + loopAround: 1, + oneToOne: 0, + speedScaling: 24 +}, /*require('Storage').readJSON("repl.settings.json", true) || */{}); + +// Key Maps for Keyboard +var KEYMAPLOWER = [ + "`1234567890-=\b\b", + "\tqwertyuiop[]\n\n", + "\2asdfghjkl;'#\x82\n", + "\2\\zxcvbnm,./\x80\x83\x81", +]; +var KEYMAPUPPER = [ + "¬!\"£$%^&*()_+\b\b", + "\tQWERTYUIOP{}\n\n", + "\2ASDFGHJKL:@~\x82\n", + "\2|ZXCVBNM<>?\x80\x83\x81", +]; +var KEYIMGL = Graphics.createImage(` + + # # + ## # +###### + ## # + # # + + + # + ### + ##### + # + # + # + # + # + # + # + # + # + # +`); +KEYIMGL.transparent = 0; +var KEYIMGR = Graphics.createImage(` + + + # + ## + ######### + ## + # + + + + ####### + # + # + # + # + # + ##### + ### + # + + # + ### + ##### + + + # # + ## ## +### ##### ### + ## ### ## + # # # + +`); +KEYIMGR.transparent = 0; +/* If a char in the keymap is >=128, +subtract 128 and look in this array for +multi-character key codes*/ +var KEYEXTRA = [ + String.fromCharCode(27, 91, 68), // 0x80 left + String.fromCharCode(27, 91, 67), // 0x81 right + String.fromCharCode(27, 91, 65), // 0x82 up + String.fromCharCode(27, 91, 66), // 0x83 down + String.fromCharCode(27, 91, 53, 126), // 0x84 page up + String.fromCharCode(27, 91, 54, 126), // 0x85 page down +]; + +// state +const R = Bangle.appRect; +var kbx = 0, + kby = 0, + kbdx = 0, + kbdy = 0, + kbShift = false, + flashToggle = false; +const PX = 12, + PY = 16, + DRAGSCALE = settings.speedScaling; +var xoff = 0, + yoff = g.getHeight() - PY * 4; + +function draw() { + "ram"; + var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER; + //g.drawImage(KEYIMG,0,yoff); + g.reset().setFont("6x8:2"); + g.clearRect(R.x, yoff, R.x2, R.y2); + if (kbx >= 0) + g.setColor(g.theme.bgH).fillRect(xoff + kbx * PX, yoff + kby * PY, xoff + (kbx + 1) * PX - 1, yoff + (kby + 1) * PY - 1).setColor(g.theme.fg); + g.drawImage(KEYIMGL, xoff - 1, yoff + PY, { scale: 2 }); + g.drawImage(KEYIMGR, xoff + PX * 12, yoff, { scale: 2 }); + var replace = /[\x80\x81\x82\x83\x84\x85]/g; + g.drawString(map[0].replace(replace," "), xoff, yoff); + g.drawString(map[1].replace(replace," "), xoff, yoff + PY); + g.drawString(map[2].replace(replace," "), xoff, yoff + PY * 2); + g.drawString(map[3].replace(replace," "), xoff, yoff + PY * 3); + g.flip(); +} + +function startTerminal(dataOutCallback) { + g.reset().clearRect(R); + // Set up the terminal + term = require("VT100").connect(g, { + charWidth: 6, + charHeight: 8 + }); + term.oy = R.y; + term.h = yoff; // we added this - it's not usually part of it + term.consoleHeight = 0 | ((term.h - term.oy) / term.charH); + term.scrollDown = function() { + g.setClipRect(R.x, term.y, R.x2, term.oy + term.h); + g.scroll(0, -this.charH); + g.setClipRect(R.x, R.y, R.x2, R.y2); + this.y--; + }; + term.fgCol = g.theme.fg; + term.bgCol = g.theme.bg; + draw(); + var flashInterval = setInterval(() => { + flashToggle = !flashToggle; + draw(); + }, 1000); + + function keyPress(kbx, kby) { + var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER; + var ch = map[kby][kbx]; + if (ch == "\2") + kbShift = !kbShift; + else { + if (ch.charCodeAt(0) > 127) + ch = KEYEXTRA[ch.charCodeAt(0) - 128]; + dataOutCallback(ch); + } + Bangle.buzz(20); + draw(); + } + + Bangle.setUI({ + mode: "custom", + drag: e => { + if (settings.oneToOne) { + kbx = Math.max(Math.min(Math.floor((e.x - 16) / (6 * 2)), 13), 0); + kby = Math.max(Math.min(Math.floor((e.y - 120) / (8 * 2)), 3), 0); + //print(e.y, kby, e.x, kbx); + } + + if (!settings.oneToOne) { + kbdx += e.dx; + kbdy += e.dy; + var dx = Math.round(kbdx / DRAGSCALE), + dy = Math.round(kbdy / DRAGSCALE); + kbdx -= dx * DRAGSCALE; + kbdy -= dy * DRAGSCALE; + if (dx || dy) { + if (settings.loopAround) { + kbx = (kbx + dx + 15) % 15; + kby = (kby + dy + 4) % 4; + } else { + kbx = Math.max(Math.min((kbx + dx), 13), 0); + kby = Math.max(Math.min((kby + dy), 3), 0); + } + } + } + draw(); + + if (!e.b && e.y > Bangle.appRect.y && settings.oneToOne /*&& settings.releaseToSelect*/ ) + keyPress(kbx, kby); + }, + touch: () => { + if (!settings.oneToOne /*|| !settings.releaseToSelect*/ ) + keyPress(kbx, kby); + } + }); + let catchSwipe = () => { + E.stopEventPropagation && E.stopEventPropagation(); + }; + Bangle.prependListener && Bangle.prependListener('swipe', catchSwipe); // Intercept swipes on fw2v19 and later. Should not break on older firmwares. + return { + dataReceived : function(d) { + g.reset().setFont("6x8"); + // USB.write(e); // optionally mirror back to the PC + // Send characters to the terminal + for (var i in d) term.char(d[i]); + // update the screen + g.flip(); + } + }; +} + +function mainMenu() { + E.showMenu({ + "":{title:"Terminal"}, + /*LANG*/"JS REPL" : function() { + var t = startTerminal(function(d) { + LoopbackB.write(d); + }); + LoopbackB.on('data', function(d) { + t.dataReceived(d); + }); + // Now move the console to Loopback + LoopbackA.setConsole(); + }, + /*LANG*/"Bluetooth" : function() { + Bangle.setUI(); + E.showMessage(/*LANG*/"Scanning...", /*LANG*/"Bluetooth"); + NRF.findDevices(function(devices) { + if (!devices.length) + return E.showAlert("No devices found").then(() => mainMenu()); + var menu = { "" : { title: /*LANG*/"Bluetooth", back : () => mainMenu() } }; + devices.forEach(dev => { + var name = dev.name || dev.id.substr(0,17); + menu[name] = function() { + Bangle.setUI(); + E.showMessage(/*LANG*/"Connecting...", /*LANG*/"Bluetooth"); + require("ble_uart").connect(dev).then(function(uart) { + var t = startTerminal(function(d) { + uart.write(d); + }); + t.dataReceived("Connected to:\n "+name+"\n") + uart.on('data', function(d) { t.dataReceived(d); }); + }).catch(err => { + E.showAlert(err.toString()).then(() => mainMenu()); + }); + }; + }); + E.showMenu(menu); + }, { filters: [{ services: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e'] }], timeout: 2000, active:true }); + } + }); +} + +mainMenu(); \ No newline at end of file diff --git a/apps/terminal/app.png b/apps/terminal/app.png new file mode 100644 index 0000000000000000000000000000000000000000..ed883aede3a246764de84dcc9f4036f404988640 GIT binary patch literal 393 zcmV;40e1e0P)@#0VE;Gug& z_7g(svU&(ld2$2s;D8z~Mu6m;hq48Isi-*x6j@{%<0p5=i*>*vl7w6jWM3Y#@>X4l4j&p@2>7ataBK n(KU(sFH~;*=Pn_H7@Oc7(T{9Dl6PQU00000NkvXXu0mjfRKcLP literal 0 HcmV?d00001 diff --git a/apps/terminal/metadata.json b/apps/terminal/metadata.json new file mode 100644 index 000000000..d32d00927 --- /dev/null +++ b/apps/terminal/metadata.json @@ -0,0 +1,14 @@ +{ "id": "terminal", + "name": "VT100 Terminal", + "shortName":"Terminal", + "version":"0.01", + "description": "Terminal and Keyboard that can be used as a REPL. You can type JS commands into Bangle.js's own REPL and execute them, or you can connect to other Bluetooth LE UART devices (like other Espruinos) and issue commands.", + "icon": "app.png", + "tags": "terminal,tool,bluetooth", + "screenshots" : [ { "url":"screenshot1.png" }, { "url":"screenshot2.png" } ], + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"terminal.app.js","url":"app.js"}, + {"name":"terminal.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/terminal/screenshot1.png b/apps/terminal/screenshot1.png new file mode 100644 index 0000000000000000000000000000000000000000..5b38b2ab775e7e7baa3c7cf97f4abfe018c98cdd GIT binary patch literal 2337 zcmeHJ{Xf%-9-nQsO_8CLZIOjePr4)12i+PIe9w!uYoLiQghkK}~ z(`FS>qOWHTCd$*udfnIO_4&MC@7L$^`Q`n2|M1DUR9Au>h{HqAfj^l?`o2sebA!L!TMrW}(c|8h^KCWhI^Dyb$w%rvJ(?QdMR^wi>@N3|tH znX1U3&!!w9J(e2Wxv?~wlz66ax9`De;(&i;^%u?YXX>w;_8_lKw$~bN{_{jOUo)hr zx!kyJ_X@8_;0d^GWYGs$vF?Cd?Nx&p_7#}&SU=~{9Pn%0>$$bnz*14YEBN(W@zX@v z-y9G)OLw+r^LX@hcO$q?)!}P#KOgyK5DN{j%+|#{IRRZ?T6*!BbLgq@j}+R*7cn#)Y1~jjGC|%M5Jh0@*u*kuT2QE-^X9++JoL z>=_fmzNLWmk}ku4_pjG#0F$`cC*>_r4M~>Cg4WyH^dicA@(2N%G;vpv$~iD*>^+5& zAyow5#%+EJgMUL@mABlo2Q2EUoo76Wz@lF5d)E9HGOqK{Ptljg0cY-0lx&GbkMc&- zj9S^VQK*05Bw3$l5k8HKr>m@_{(44{pYyI6ta>J70@$^8=4O;D7>lVLp$ESJH9@S6ipR1%{XRxm!^+#hfQp3c4vRZ;swa{n zYC9ES!_+h$b&P-c$@PByD|ao&Bh*8>?A9s0w%+K$Y2bUI7}ae+u&%-2TrCpTfr;>b z-5Uz)!niFpNM2*R7!VNT4?CNVsVl(kMuH# z7ccPE=rdwsoxFn*HqvLL5zom<(b9=-T7GESYNdJk-42)jc^r41T^TJcoi0dU(5-&1 zCo3r-Hr~TVejjr!>uSg2>`@QyHEAS#A|nj5GTBY!dF|%X_A@8GtwvvEr@WMYzDeUV zislWz))oOn@dDFVH}_YE*>4xvHOO^n$vv;2>LX->d^}PG{lJPI9(!O#=?CXYgOiVo zK>zW{#zdOg0SfakGTovPboO8W)kzE(11{jXa^muvC16OS%@EiFdGoH_$cRpFwneZm z&~+au14rx3Q3G!wPVCBrm`nld1`x&D-74t4uoec{{LyTuE0p4=WTeox-a#)Uz=#?P z!c!uCE{Jfil7@>sZb>qI+>fl~XNd6@Z<+bV!nxM6a>o&dm0RgfHu$@nO)l|1qFs0U z3GR^vnLUao5A4kRot*P&FZjsz5QM2_21*3UCZW2b&C4N0 z8JC%&Hd1>aX1|B|N9uFgzWT(GK3WEH>HcwR&UU6{eCwV0Tu7Rf;x@e)m)qto7cTSH z9%+#wChInoY-*fO{Ni>(*k*$CB&YdMfl)Lp9i4b?6L~y7#_9eJlP`d)@#;d?3u;@B zoILdocI4|~sb4KGh0nl7rbFHvep_)a|J_%dIkeQx(K#A3rl&~8%%{t*o21&Rg^SBP zQ1ut5=i7XTI}TjAKpRn6m;*kFwPbsK9f-iH2VlIYS!OF-v%aw*&V&2TE-f4>oZa@w3xM ztOLoy-xN*fgpB(FZ?Q;()#EO}6;x}%2&Q@EYw4-sOTwzT1QE4BN9f>+C&%b^-5hf< z*NSH)k!7qxr~bP2!}tM8n_J#i@>bROh9picQ){#lLGV#(^LdD~Kpy96!#nLkuvlhX zp_-6?KpR%r?ht&z;!hipUOs7M)U;X~^OOTE%Jb(r9E^zH^Or-4%!RY!AEAR-2#oXx zBc=8zvCawqai;i~1{a>1;zr&!NreY!+5&kM?S{s5`qU4I2aX_>pk5Rx$Gt@u1b)%I(56JPHlRd}I H@9+Nxifcsj literal 0 HcmV?d00001 diff --git a/apps/terminal/screenshot2.png b/apps/terminal/screenshot2.png new file mode 100644 index 0000000000000000000000000000000000000000..e6d94d8d263d35016d6cfc68f6041ffaf9aeb90b GIT binary patch literal 3204 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 Ee@}Vz>% literal 0 HcmV?d00001 From a4c822c0f584b51de2d66205c241d8259c895f79 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 22 Jan 2024 14:45:54 +0000 Subject: [PATCH 44/70] swap screenshots so more important one is first --- apps/terminal/screenshot1.png | Bin 2337 -> 3204 bytes apps/terminal/screenshot2.png | Bin 3204 -> 2337 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/terminal/screenshot1.png b/apps/terminal/screenshot1.png index 5b38b2ab775e7e7baa3c7cf97f4abfe018c98cdd..e6d94d8d263d35016d6cfc68f6041ffaf9aeb90b 100644 GIT binary patch literal 3204 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 Ee@}Vz>% literal 2337 zcmeHJ{Xf%-9-nQsO_8CLZIOjePr4)12i+PIe9w!uYoLiQghkK}~ z(`FS>qOWHTCd$*udfnIO_4&MC@7L$^`Q`n2|M1DUR9Au>h{HqAfj^l?`o2sebA!L!TMrW}(c|8h^KCWhI^Dyb$w%rvJ(?QdMR^wi>@N3|tH znX1U3&!!w9J(e2Wxv?~wlz66ax9`De;(&i;^%u?YXX>w;_8_lKw$~bN{_{jOUo)hr zx!kyJ_X@8_;0d^GWYGs$vF?Cd?Nx&p_7#}&SU=~{9Pn%0>$$bnz*14YEBN(W@zX@v z-y9G)OLw+r^LX@hcO$q?)!}P#KOgyK5DN{j%+|#{IRRZ?T6*!BbLgq@j}+R*7cn#)Y1~jjGC|%M5Jh0@*u*kuT2QE-^X9++JoL z>=_fmzNLWmk}ku4_pjG#0F$`cC*>_r4M~>Cg4WyH^dicA@(2N%G;vpv$~iD*>^+5& zAyow5#%+EJgMUL@mABlo2Q2EUoo76Wz@lF5d)E9HGOqK{Ptljg0cY-0lx&GbkMc&- zj9S^VQK*05Bw3$l5k8HKr>m@_{(44{pYyI6ta>J70@$^8=4O;D7>lVLp$ESJH9@S6ipR1%{XRxm!^+#hfQp3c4vRZ;swa{n zYC9ES!_+h$b&P-c$@PByD|ao&Bh*8>?A9s0w%+K$Y2bUI7}ae+u&%-2TrCpTfr;>b z-5Uz)!niFpNM2*R7!VNT4?CNVsVl(kMuH# z7ccPE=rdwsoxFn*HqvLL5zom<(b9=-T7GESYNdJk-42)jc^r41T^TJcoi0dU(5-&1 zCo3r-Hr~TVejjr!>uSg2>`@QyHEAS#A|nj5GTBY!dF|%X_A@8GtwvvEr@WMYzDeUV zislWz))oOn@dDFVH}_YE*>4xvHOO^n$vv;2>LX->d^}PG{lJPI9(!O#=?CXYgOiVo zK>zW{#zdOg0SfakGTovPboO8W)kzE(11{jXa^muvC16OS%@EiFdGoH_$cRpFwneZm z&~+au14rx3Q3G!wPVCBrm`nld1`x&D-74t4uoec{{LyTuE0p4=WTeox-a#)Uz=#?P z!c!uCE{Jfil7@>sZb>qI+>fl~XNd6@Z<+bV!nxM6a>o&dm0RgfHu$@nO)l|1qFs0U z3GR^vnLUao5A4kRot*P&FZjsz5QM2_21*3UCZW2b&C4N0 z8JC%&Hd1>aX1|B|N9uFgzWT(GK3WEH>HcwR&UU6{eCwV0Tu7Rf;x@e)m)qto7cTSH z9%+#wChInoY-*fO{Ni>(*k*$CB&YdMfl)Lp9i4b?6L~y7#_9eJlP`d)@#;d?3u;@B zoILdocI4|~sb4KGh0nl7rbFHvep_)a|J_%dIkeQx(K#A3rl&~8%%{t*o21&Rg^SBP zQ1ut5=i7XTI}TjAKpRn6m;*kFwPbsK9f-iH2VlIYS!OF-v%aw*&V&2TE-f4>oZa@w3xM ztOLoy-xN*fgpB(FZ?Q;()#EO}6;x}%2&Q@EYw4-sOTwzT1QE4BN9f>+C&%b^-5hf< z*NSH)k!7qxr~bP2!}tM8n_J#i@>bROh9picQ){#lLGV#(^LdD~Kpy96!#nLkuvlhX zp_-6?KpR%r?ht&z;!hipUOs7M)U;X~^OOTE%Jb(r9E^zH^Or-4%!RY!AEAR-2#oXx zBc=8zvCawqai;i~1{a>1;zr&!NreY!+5&kM?S{s5`qU4I2aX_>pk5Rx$Gt@u1b)%I(56JPHlRd}I H@9+Nxifcsj diff --git a/apps/terminal/screenshot2.png b/apps/terminal/screenshot2.png index e6d94d8d263d35016d6cfc68f6041ffaf9aeb90b..5b38b2ab775e7e7baa3c7cf97f4abfe018c98cdd 100644 GIT binary patch literal 2337 zcmeHJ{Xf%-9-nQsO_8CLZIOjePr4)12i+PIe9w!uYoLiQghkK}~ z(`FS>qOWHTCd$*udfnIO_4&MC@7L$^`Q`n2|M1DUR9Au>h{HqAfj^l?`o2sebA!L!TMrW}(c|8h^KCWhI^Dyb$w%rvJ(?QdMR^wi>@N3|tH znX1U3&!!w9J(e2Wxv?~wlz66ax9`De;(&i;^%u?YXX>w;_8_lKw$~bN{_{jOUo)hr zx!kyJ_X@8_;0d^GWYGs$vF?Cd?Nx&p_7#}&SU=~{9Pn%0>$$bnz*14YEBN(W@zX@v z-y9G)OLw+r^LX@hcO$q?)!}P#KOgyK5DN{j%+|#{IRRZ?T6*!BbLgq@j}+R*7cn#)Y1~jjGC|%M5Jh0@*u*kuT2QE-^X9++JoL z>=_fmzNLWmk}ku4_pjG#0F$`cC*>_r4M~>Cg4WyH^dicA@(2N%G;vpv$~iD*>^+5& zAyow5#%+EJgMUL@mABlo2Q2EUoo76Wz@lF5d)E9HGOqK{Ptljg0cY-0lx&GbkMc&- zj9S^VQK*05Bw3$l5k8HKr>m@_{(44{pYyI6ta>J70@$^8=4O;D7>lVLp$ESJH9@S6ipR1%{XRxm!^+#hfQp3c4vRZ;swa{n zYC9ES!_+h$b&P-c$@PByD|ao&Bh*8>?A9s0w%+K$Y2bUI7}ae+u&%-2TrCpTfr;>b z-5Uz)!niFpNM2*R7!VNT4?CNVsVl(kMuH# z7ccPE=rdwsoxFn*HqvLL5zom<(b9=-T7GESYNdJk-42)jc^r41T^TJcoi0dU(5-&1 zCo3r-Hr~TVejjr!>uSg2>`@QyHEAS#A|nj5GTBY!dF|%X_A@8GtwvvEr@WMYzDeUV zislWz))oOn@dDFVH}_YE*>4xvHOO^n$vv;2>LX->d^}PG{lJPI9(!O#=?CXYgOiVo zK>zW{#zdOg0SfakGTovPboO8W)kzE(11{jXa^muvC16OS%@EiFdGoH_$cRpFwneZm z&~+au14rx3Q3G!wPVCBrm`nld1`x&D-74t4uoec{{LyTuE0p4=WTeox-a#)Uz=#?P z!c!uCE{Jfil7@>sZb>qI+>fl~XNd6@Z<+bV!nxM6a>o&dm0RgfHu$@nO)l|1qFs0U z3GR^vnLUao5A4kRot*P&FZjsz5QM2_21*3UCZW2b&C4N0 z8JC%&Hd1>aX1|B|N9uFgzWT(GK3WEH>HcwR&UU6{eCwV0Tu7Rf;x@e)m)qto7cTSH z9%+#wChInoY-*fO{Ni>(*k*$CB&YdMfl)Lp9i4b?6L~y7#_9eJlP`d)@#;d?3u;@B zoILdocI4|~sb4KGh0nl7rbFHvep_)a|J_%dIkeQx(K#A3rl&~8%%{t*o21&Rg^SBP zQ1ut5=i7XTI}TjAKpRn6m;*kFwPbsK9f-iH2VlIYS!OF-v%aw*&V&2TE-f4>oZa@w3xM ztOLoy-xN*fgpB(FZ?Q;()#EO}6;x}%2&Q@EYw4-sOTwzT1QE4BN9f>+C&%b^-5hf< z*NSH)k!7qxr~bP2!}tM8n_J#i@>bROh9picQ){#lLGV#(^LdD~Kpy96!#nLkuvlhX zp_-6?KpR%r?ht&z;!hipUOs7M)U;X~^OOTE%Jb(r9E^zH^Or-4%!RY!AEAR-2#oXx zBc=8zvCawqai;i~1{a>1;zr&!NreY!+5&kM?S{s5`qU4I2aX_>pk5Rx$Gt@u1b)%I(56JPHlRd}I H@9+Nxifcsj literal 3204 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 Ee@}Vz>% From a98ce79c43e3ec55a1ace361e2cb1916b7880d8d Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 22 Jan 2024 15:30:17 +0000 Subject: [PATCH 45/70] Add new, more useful spirit level app --- apps/angles/ChangeLog | 1 + apps/angles/app.js | 49 ++++++++++++++++++++++++++++++++ apps/angles/icon.js | 1 + apps/angles/icon.png | Bin 0 -> 12090 bytes apps/angles/metadata.json | 15 ++++++++++ apps/angles/screenshot.png | Bin 0 -> 2433 bytes apps/slevel/spiritlevel-icon.js | 3 +- apps/slevel/spiritlevel.png | Bin 5214 -> 12090 bytes 8 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 apps/angles/ChangeLog create mode 100644 apps/angles/app.js create mode 100644 apps/angles/icon.js create mode 100644 apps/angles/icon.png create mode 100644 apps/angles/metadata.json create mode 100644 apps/angles/screenshot.png diff --git a/apps/angles/ChangeLog b/apps/angles/ChangeLog new file mode 100644 index 000000000..2286a7f70 --- /dev/null +++ b/apps/angles/ChangeLog @@ -0,0 +1 @@ +0.01: New App! \ No newline at end of file diff --git a/apps/angles/app.js b/apps/angles/app.js new file mode 100644 index 000000000..a07c29199 --- /dev/null +++ b/apps/angles/app.js @@ -0,0 +1,49 @@ +g.clear().setRotation(1); +// g.setRotation ALSO changes accelerometer axes +var avrAngle = undefined; +var history = []; + +var R = Bangle.appRect; +var W = g.getWidth(); +var H = g.getHeight(); +var relativeTo = undefined; + +function draw(v) { + if (v===undefined) v = Bangle.getAccel(); + // current angle + var d = Math.sqrt(v.y*v.y + v.z*v.z); + var ang = Math.atan2(-v.x, d)*180/Math.PI; + // Median filter + if (history.length > 10) history.shift(); // pull old reading off the start + history.push(ang); + avrAngle = history.slice().sort()[(history.length-1)>>1]; // median filter + // Render + var x = R.x + R.w/2; + var y = R.y + R.h/2; + g.reset().clearRect(R).setFontAlign(0,0); + var displayAngle = avrAngle; + g.setFont("6x15").drawString("ANGLE (DEGREES)", x, R.y2-8); + if (relativeTo!==undefined) { + g.drawString("RELATIVE TO", x,y-50); + g.setFont("Vector:30").drawString(relativeTo.toFixed(1),x,y-30); + y += 20; + displayAngle = displayAngle-relativeTo; + } + g.setFont("Vector:60").drawString(displayAngle.toFixed(1),x,y); + +} + +draw(); +Bangle.on('accel',draw); + +// Pressing the button turns relative angle on/off +Bangle.setUI({ + mode : "custom", + btn : function(n) { + if (relativeTo===undefined) + relativeTo = avrAngle; + else + relativeTo = undefined; + draw(); + } +}); \ No newline at end of file diff --git a/apps/angles/icon.js b/apps/angles/icon.js new file mode 100644 index 000000000..3f051f95f --- /dev/null +++ b/apps/angles/icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4cA///ov+5lChWMyGuxdzpdj4/lKf4AUkgQPgm0wAiPy2QCBsBkmS6QRNhIRBrVACJlPu2+pdICBcCrVJlvJtIRLifStMl3MtkARKydUyMkzMl0CMKyWWyUk1MkSJXkyR7BogRLgVcydSrVGzLHKgdLyfSpdE3JYKklqTwNJknJYJVkxcSp+pnygKhMs1OSEQOSYhVJl1bCIbBK5Mq7gRCyARJiVbqyPBCIKMKuVM24yBCIIiJnVOqu5CISMKp9JlvJCIRXKpP3nxoCRhUSBwSMNBwaMMgn6yp6DRhUl0mypiMMgM9ksipaMMhMtCINKRhlJmoRBpJuBCBIRGRhUE5I1CpKMLgmZn5ZDGhUAycnRoNMRhTDCsn3tfkRhLnDTwYQLNgSMMUQkyRhbGEkyMKAApFOAH4AGA")) \ No newline at end of file diff --git a/apps/angles/icon.png b/apps/angles/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1a4559d44f503572e8b11823b5f775a2fac46068 GIT binary patch literal 12090 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 F`9BuIC9wbi literal 0 HcmV?d00001 diff --git a/apps/angles/metadata.json b/apps/angles/metadata.json new file mode 100644 index 000000000..f8a90a305 --- /dev/null +++ b/apps/angles/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "angles", + "name": "Angles (Spirit Level)", + "shortName": "Angles", + "version": "0.01", + "description": "Shows Angle or Relative angle in degrees (Digital Protractor/Inclinometer). Place Bangle sideways against a surface with the button facing away for best readings.", + "icon": "icon.png", + "screenshots": [{"url":"screenshot.png"}], + "tags": "tool", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"angles.app.js","url":"app.js"}, + {"name":"angles.img","url":"icon.js","evaluate":true} + ] +} diff --git a/apps/angles/screenshot.png b/apps/angles/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..9d631cf74b04dec9d3223278ea1bf805127a22de GIT binary patch literal 2433 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 F`9BuIC9wbi literal 5214 zcmZWtbyQSev>sw$h7e{5=^PqHLKJWikfEeQLc#%t5|R7?Lr5brG|~(uAt8-)NX-xu zDiVT(lp-l52yc9UzPIkWcb~QP+28)Yv+iE!oE>YRr$I}_Mg;%>XtgwvMns$Z?*&m1 zznki-twcj%r>%hmT>pCtyUNo507eEaq>71u-miR&2gm39;hx&^%}>}wRu`CdLp@ev zMSF;{h2fd^ER03+6R9OrU6oOvc#-d2?W@_xqJe1Z3;jyw42O6Lp#xKB9F@bqAkYLL zv`j7eb73~a&1WNapPtG z%KtTu45@ph9?&8uHnzIArLX8G&+6Sea6JVEhkyz@LHeBHve~_)u5(AZf_3s%Wgw-Z zWe>Q)gFdo5BJlBx)?ag*Vj$iN@D^EGEx+l^5S&q&W|ebQ1lzP69|%aq*ceVJdIWGdQ=r#aF0* z>#5c0@P{(*2zjv`7%?$G8z-mzl_Y(kMA#&@RT(3gjqs$h;DZ}*P4VgHJ$MyEoi6kn z&rnV;uu8ff=JP$)*cs{>=0)?5Z`{5Vh+})HBpF8ZC0icy1Y=eiJQRW46psJ?04H~z8bep+2v$5KFQLfmu!-I^6hrVbAXFfZhAM4} zq?wA*pN|wPB&b~icKb$O`TyOwGpPLY(a{HxL7fh4!mEnxJG`XkzBArmx&+nqIxHw| z5s$oJk3RVR!)A8eYueTOU#5QNl+DAtgR`e3PMC-AU=`8jBM_gGaY_adgy;m*=hds166{f-&+ zo%7|7O=+R>Oz}R=M~CO5THRixR)x2x<|i8WjudX6-jtbm^f~9?>;u)O8H=gbZmM5q z>dQw+=v!knn534nkkQc+(s<`7yU+5}t6<649}Q-O5Pych|f2Lnj}8N~xGNJFhq zLvqv4i9!;AUa@$geBDhY{eTFUicVSir#g65qY*N|@WKeMSnMGSa=Al4v)?)A&xCt( ztp6_eu8OX8><`6Zjc9tRv+T|LuK$&aLIJ3YIFd_$*L$FBQ%gty@*}NaA8%>A_Qy>#Pc~*QA21-Z%6BV>w_NEeQ_FBKU%iXVkXzOz0$U~xBkhKU1 zA@pwISHjNGwo6BJ7|v7ti5UUwWYqNmtrcE_CyAKK^4Lv0r2io{C;Q1~wYE{_OI{5< z_MQSfpVs-$GR&X$HA&YYpx1m76a~4!r$(B?u-fH+_CGAe8!-A#y8hF#ew@`4W6c00 z+hP7vm$YbPZ2wQe9-Th!;-h5`%gk|Ku1A0&)F3ra9b zKLDd(h59^zy4-aqkFu4_^dF~T)aj93*9WuTtbU06Wpo+a1P{(mkvu9(v@T7>+sQ}^ z7@<#C%!PBFn>@hvwfza`Dp)0bQ+N7V!v^91W;G!sAf)Zst~NlqN4UevwQ$MZI3o%nfTU`2@| z1_;$4L}%Cs+-H1AeLLZ$?{4>9!L%fYDD&0f=@h)(>$7D$+2ZL>4{`M2nlN&a9DnX# z(1iESo?(&{J-)Y3)^3;?7_&lZG1KXk%cQ!t7a#Hmsbq0{%G|nr#ZVnjT3)vwLre?0s!~L!Klx4G<0ENq}|S9`f~6%b-WSHlw{z` z_A9P}N~B5;mOq3=RXj(SkaNz3=nUd1;3@Yd0p#Nx%ZTPyiN|z~roYz3z)xrp*=hI&2asc9B);hK+R<@NHm7B|!Q&$72 z7bA z=4C{m{sl#@3N2VJ`@(7-m+QZgHAHrL^_Ayrb68F7~3 z6vw-K(2j+1o;V`rim-Q|;l=|Z<+%KHVQ;fOo5vIudRH=vJ?Ct@?wShF6ei+=(gRS} z=Zc>NQ#giTnAP-lWV%oa9%l5cnq4;_+o)l717(Y$uOah26y6q76*^KDbj(wo{&ss(W;4V&50;#AtHK*%LBsR+CJ+jZT!pc&QA zoZFQ3-;!OL?X5)HXOq5FC6hlnH3D6!8KXD@S=4}mzCj74kLFF4&?%iGWMf`oHs{O7 zL(7H7pOzE&GRGki%`uC^_VrrA?dJ+dfx%nO#+e$o4K9CAIaWWx<0jHn58-r@@xXxf zh%fA9WEme<3Y{qgSlFBR+K|%WeV%zrp0o`kk;h<6_t20U;U(NlS%oOYD69<6AQXiR zUW5ewxe9&SJGO2s^OJl&9?HdQ0gQ6>=1#ua`EucR}|9{9@!s>6Q;+ z_NHK;e`TDed1GN`y0e}{EHHwQtIB+kQ&D>_JZXv5)XmKklj^-d)%5Gu6XEU7fZdt> zv+rf?bE5r*Y--Z6Lliap=R=;t1lLitL7kI^%$?U+Z&S#qjJTHXFl`m(ckBs}>cMU5 z+nd)8&Bm)=;7p+BUH9{pFp4g{m$#C+yo|<%(yZ2Zj&!i)5S`vu-Awc zuUyfQkvZ+)@jEULb$!?gGHYrfZObhX=e??=PXD?$UhZ;^S<;seL^PeeLGX8wO6X!x z{(tWVC+EKgbl{EaYHJ8znMtQ)XipzwN8FR{bm&6u-x)s)Gl3@D0D2yp)9t~=B!RaK zfbeyCrNd`eI@*gL8&F&eA1Mun)+FQ4{dj&k+G!J zYbmLOJMo|_AVYW->_G6P=s47sx;p4uugsNmiTZ+&Dd%x5G+7vF@x7?VOP6>Vp+@CIciP{&>fcHIS<#F7U@$5KhNHlC(6p zaK@UWFXIoyhjw9dPnRRKU@R`j^KxO$Y^v7@>WB>o6>o2sS}JdA@M!9g7DdE&3CPVn z4l7%-qxmytQbhh-Ba#SjN37E74*tF78}!^b^KX|H-PEbzL%0Fl*#0J@0vU{ za|t1VUiYtjN>j51jS>!vLB4J~eSVpAy}jmZILZwyLiq;yG~dKe z>6XirWJ}s_ud6T2q4fx4XpAubD+HCm*>NWILDd@dqoYb!2XW{3D~@-6hy`bv72m4T zi_Y>s1XwU?YL6d{2+^MPmtt509ZjGf)q@G^pYNNzdi|9Oo;Ie12^9ywR>S#C%&eYn zx*9EwI^NaMG5(L$_BJ?1jj`fc9to=0^xo%no$P3yh%pP|^a16^-(A`bu|3y|4^Ju$ z0qI6(iv0d44cXncTCWj#5%q(a5bLln_Th7txYYnd3?R_YCNz0*<0>zM62E`L;rFw@ zf_Q>Zgdx?=MX}x^9Pz~HuCO4fFiF=D@#$Q2uMLp5EnPmhtO6LqpqL`k*lEL+je6C%l8;|`|AXlL9fuI=?7P!yMV;mjTJHkPbc znh>{8Op)-RMuLQA?p|B-z^)$eIpwSEm|>lBro=WTPNf=L*~)eOXosIzvT2D?%%AKW z?FyZ`&0h8agUO24MM*8<9Gf_30H_9Y6g>k_8pNoV+mcAg^}zTfCn_TWOGWA8a~E^@ zll)UpUo~ku#xtpVA3iF5Qq!xMlx$)kmxSDTztLSfL%5a`u z>&DP*K1)X7I}%(|UhHuFoGs>cN~e=>2LiA4UmdU6#FCUAE=gwz#4ZOu!e>FhJ+PO zV^nbdU#`|@>+RF3>toMrm9VO@Q%($+2H!)@*z68X_#IC zUcheCX}72$lTz`Cxxb|}!g<>d#WT2T_l6!c=$-p^ElR@L3dbQ`qEexwiDqU(%u15y zTBt7+k{N1+eA{Ti+`mKzQgb=8JoIJF6rx6>%Rc+zFe~tf>HL5!T}cTk2;hvLqVx0- zZDNZ_p`T9hAW$+zzO8;fp0ldE@7pF From 60bab26d838a2b45d15c734741bd00ab26e7d2b9 Mon Sep 17 00:00:00 2001 From: Anonymous941 Date: Mon, 22 Jan 2024 10:52:51 -0500 Subject: [PATCH 46/70] tetris: bug fixes, add back swipe controls --- apps/tetris/tetris.app.js | 132 ++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index baad84f77..1917dade8 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -115,7 +115,7 @@ function calculateSpeed() { step = 20; // usually limited by the hardware // levels 15+ are programmed to go faster by skipping lines } - print(`level ${level}: drop interval ${step}ms`) + print(`level ${level}: drop interval ${step}ms`); if (control == 3) step = step*2; dropInterval = step; @@ -272,59 +272,80 @@ function linear(x) { function newGame() { E.showMenu(); - Bangle.setUI(); - if (control == 2) { - Bangle.on("accel", (e) => { - if (state != 1) return; - if (control != 2) return; - print(e.x); - linear((0.2-e.x) * 2.5); - }); - } - if (control == 3) { - Bangle.setBarometerPower(true); - Bangle.on("pressure", (e) => { - if (state != 1) return; - if (control != 3) return; - let a = e.altitude; - if (alt_start == -9999) - alt_start = a; - a = a - alt_start; - //print(e.altitude, a); - linear(a); - }); - } - Bangle.on("drag", (e) => { - let h = 176/2; - if (state == 2) { - if (e.b) - selectGame(); - return; - } - if (!e.b) - return; - if (state == 0) return; - if (e.y < h) { - if (e.x < h) - rotate(); - else { - while (move(0, 1)) { - score++; - g.flip(); + Bangle.setUI({mode : "custom", btn: () => load()}); + if (control == 4) { // Swipe + Bangle.on("touch", (e) => { + t = rotateTile(ct, 3); + if (moveOk(t, 0, 0)) { + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + ct = t; + drawTile(ct, ctn, ox+px*8, oy+py*8, false); } - redrawStats(true); + }); + + Bangle.on("swipe", (x,y) => { + if (y<0) y = 0; + if (moveOk(ct, x, y)) { + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + px += x; + py += y; + drawTile(ct, ctn, ox+px*8, oy+py*8, false); + } + }); + } else { // control != 4 + if (control == 2) { // Tilt + Bangle.on("accel", (e) => { + if (state != 1) return; + if (control != 2) return; + print(e.x); + linear((0.2-e.x) * 2.5); + }); } - } else { - if (control == 1) - linear((e.x - 20) / 156); - if (control != 0) - return; - if (e.x < h) - move(-1, 0); - else - move(1, 0); - } - }); + if (control == 3) { // Move + Bangle.setBarometerPower(true); + Bangle.on("pressure", (e) => { + if (state != 1) return; + if (control != 3) return; + let a = e.altitude; + if (alt_start == -9999) + alt_start = a; + a = a - alt_start; + //print(e.altitude, a); + linear(a); + }); + } + Bangle.on("drag", (e) => { + let h = 176/2; + if (state == 2) { + if (e.b) + selectGame(); + return; + } + if (!e.b) + return; + if (state == 0) return; + if (e.y < h) { + if (e.x < h) + rotate(); + else { + while (move(0, 1)) { + score++; + g.flip(); + } + redrawStats(true); + } + } else { + if (control == 1) + linear((e.x - 20) / 156); + if (control != 0) + return; + if (e.x < h) + move(-1, 0); + else + move(1, 0); + } + }); + } setWatch(() => { if (state == 1) pauseGame(); @@ -333,9 +354,9 @@ function newGame() { }, BTN1, {repeat: true}); initGame(); - drawGame(); - state = 1; calculateSpeed(); + state = 1; + drawGame(); var gi = setInterval(gameStep, 20); } @@ -346,8 +367,8 @@ function drawGame() { .drawString("Level", 22, 80) .drawString("Lines", 22, 130) .drawString("Next", 176-22, 30); - showNext(ntn, ntr); redrawStats(); + showNext(ntn, ntr); } function selectGame() { @@ -359,6 +380,7 @@ function selectGame() { menu["Drag"] = () => { control = 1; newGame(); }; menu["Tilt"] = () => { control = 2; newGame(); }; menu["Pressure"] = () => { control = 3; newGame(); }; + menu["Swipe"] = () => { control = 4; newGame(); }; level = 1; menu["Level"] = { value : 1, From a08e4dbd4f29199788b392834b516eb5311d55c7 Mon Sep 17 00:00:00 2001 From: Anonymous941 Date: Mon, 22 Jan 2024 13:36:45 -0500 Subject: [PATCH 47/70] tetris: add clear effect and more bug fixes --- apps/tetris/tetris.app.js | 52 +++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index 1917dade8..d90627bde 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -134,7 +134,8 @@ function gameOver() { state = 0; g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("Vector",22) .drawString("Game Over", 176/2, 76); - E.showAlert("Game Over").then(selectGame, print); + // this cannot allow changing game controls because it would set up duplicate events + E.showAlert("Game Over").then(startGame, print); lines = 0; score = 0; } @@ -151,19 +152,26 @@ function redrawStats(onlyScore) { } function insertAndCheck() { - for (y=0; y0) pf[py+y][px+x+1] = ctn+1; - // check for full lines let clearCount = 0; - for (y=19; y>0; y--) { + let linesToClear = []; + let yReal = 19; // the y for display purposes + // check for full lines + for (let y=19; y>0; y--) { var qFull = true; - for (x=1; x<11; ++x) qFull &= pf[y][x]>0; + for (let x=1; x<11; ++x) qFull &= pf[y][x]>0; if (qFull) { clearCount++; + linesToClear.push([y, yReal]); + print(`linesToClear.push(${y})`); + // clear the line, but do not display it yet for (ny=y; ny>0; ny--) pf[ny] = JSON.parse(JSON.stringify(pf[ny-1])); - redrawPF(y); + y++; } + yReal--; } if (clearCount) { lines += clearCount; @@ -178,11 +186,32 @@ function insertAndCheck() { } else if (clearCount == 3) { // triple score += 500 * effectiveLevel; - Bangle.buzz(150); + Bangle.buzz(200); } else if (clearCount >= 4) { // tetris score += 800 * effectiveLevel; - Bangle.buzz(300); + Bangle.buzz(500); + } + // the score will not be shown yet because redrawStats was not called + + // clear effect + let timer = getTime(); + g.setColor(0, 0, 0); + while (true) { + var rectLength = (getTime()-timer)/0.05 + 1; + if (rectLength > 6) + break; + var x1 = 6 - rectLength; + var x2 = 4 + rectLength; + for (let line of linesToClear) { + let y = line[1]; + g.fillRect(ox+x1*8, oy+y*8, ox+x2*8-1, oy+(y+1)*8-1); + } + g.flip(); + } + // display the cleared lines + for (let line of linesToClear) { + redrawPF(line[0]); } if (lines != 0 && lines % 10 == 0) { level++; @@ -301,7 +330,7 @@ function newGame() { linear((0.2-e.x) * 2.5); }); } - if (control == 3) { // Move + if (control == 3) { // Pressure Bangle.setBarometerPower(true); Bangle.on("pressure", (e) => { if (state != 1) return; @@ -352,7 +381,10 @@ function newGame() { else if (state == 3) resumeGame(); }, BTN1, {repeat: true}); + startGame(); +} +function startGame() { initGame(); calculateSpeed(); state = 1; From 28dd5da804c6f770aa3538cc00f4bb2f13066017 Mon Sep 17 00:00:00 2001 From: pglez82 Date: Tue, 23 Jan 2024 12:42:15 +0100 Subject: [PATCH 48/70] changing the setui call before loadwidgets call --- apps/quoteclock/app.js | 210 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 186 insertions(+), 24 deletions(-) diff --git a/apps/quoteclock/app.js b/apps/quoteclock/app.js index c4ec0e7da..63b3ea032 100644 --- a/apps/quoteclock/app.js +++ b/apps/quoteclock/app.js @@ -1,34 +1,196 @@ -const locale=require("locale"); -const quotesshakespeare=["Be not afraid of greatness. Some are born great, some achieve greatness, and others have greatness thrust upon them.","We know what we are, but know not what we may be.","Sweet are the uses of adversity which, like the toad, ugly and venomous, wears yet a precious jewel in his head.","Our doubts are traitors and make us lose the good we oft might win by fearing to attempt.","Give every man thy ear, but few thy voice.","Uneasy lies the head that wears the crown.","How poor are they that have not patience! What wound did ever heal but by degrees?","Nothing can come of nothing.","How far that little candle throws its beams! So shines a good deed in a naughty world.","What's done can't be undone.","Though she be but little, she is fierce.","No legacy is so rich as honesty.","This above all; to thine own self be true.","I wasted time, and now doth time waste me.","The robbed that smiles, steals something from the thief.","The devil can cite Scripture for his purpose.","One touch of nature makes the whole world kin.","What is past is prologue.","Small cheer and great welcome makes a merry feast.","Sweet mercy is nobility's true badge.","'Tis not enough to help the feeble up, but to support them after.","Neither a borrower nor a lender be.","Ambition should be made of sterner stuff.","I bear a charmed life.","Heat not a furnace for your foe so hot that it do singe yourself.","Talking isn't doing. It is a kind of good deed to say well; and yet words are not deeds.","In time we hate that which we often fear.","Modest doubt is called the beacon of the wise.","With mirth and laughter let old wrinkles come.","Boldness be my friend.","Words without thoughts never to heaven go.","Wisely, and slow. They stumble that run fast.","Pleasure and action make the hours seem short.","When words are scarce they are seldom spent in vain.","Such as we are made of, such we be.","And oftentimes excusing of a fault doth make the fault the worse by the excuse.","Reputation is an idle and most false imposition; oft got without merit, and lost without deserving.","To be, or not to be: that is the question.","All the world's a stage, and all the men and women merely players. They have their exits and their entrances; and one man in his time plays many parts.","All that glisters is not gold.","Words are easy, like the wind; faithful friends are hard to find.","The fault is not in our stars, but in ourselves.","And this, our life, exempt from public haunt, finds tongues in trees, books in the running brooks, sermons in stones, and good in everything.","Expectation is the root of all heartache.","I like this place and could willingly waste my time in it.","Better three hours too soon than a minute too late.","Life's but a walking shadow, a poor player, that struts and frets his hour upon the stage, and then is heard no more; it is a tale told by an idiot, full of sound and fury, signifying nothing.","My tongue will tell the anger of my heart, or else my heart concealing it will break.","Brevity is the soul of wit.","Give sorrow words; the grief that does not speak knits up o-er wrought heart and bids it break.","Look like the innocent flower, but be the serpent under it.","One may smile, and smile, be a villain.","Conscience doth make cowards of us all.","Let me be that I am and seek not to alter me.","Et tu, Brute?","O, beware, my lord, of jealousy; it is the green-ey'd monster, which doth mock the meat it feeds on.","If we are true to ourselves, we can not be false to anyone.","Be great in act, as you have been in thought.","Suspicion always haunts the guilty mind.","All things are ready, if our mind be so.","Many a true word hath been spoken in jest.","For sweetest things turn sourest by their deeds; lillies that fester smell far worse than weeds.","The Devil hath power to assume a pleasing shape.","Thought is free.","April hath put a spirit of youth in everything.","Summer's lease hath all too short a date.","Our bodies are our gardens to the which our wills are gardeners.","The tempter or the tempted, who sins most?","Men should be what they seem.","He jests at scars that never felt a wound.","I would not wish any companion in the world but you.","Self-love, my liege, is not so vile a sin, as self-neglecting.","Doubt thou the stars are fire, doubt that the sun doth move. Doubt truth to be a liar, but never doubt I love.","I am one who loved not wisely but too well.","A young woman in love always looks like patience on a monument smiling at grief.","My bounty is as boundless as the sea, my love as deep; the more I give to thee, the more I have, for both are infinite.","They do not love that do not show their love.","I love you with so much of my heart that none is left to protest.","Love is heavy and light, bright and dark, hot and cold, sick and healthy, asleep and awake.","Shall I compare thee to a summer's day? Thou art more lovely and more temperate.","Love all, trust a few, do wrong to none.","Kindness in women, not their beauteous looks, shall win my love.","Love looks not with the eyes, but with the mind, and therefore is winged Cupid painted blind.","Do not swear by the moon, for she changes constantly. Then your love would also change.","If music be the food of love, play on.","Love is too young to know what conscience is.","Did my heart love till now? Forswear it, sight! For I ne'er saw true beauty till this night.","Don't waste your love on somebody, who doesn't value it.","And yet, to say the truth, reason and love keep little company together nowadays.","Love is a smoke made with the fume of sighs.","Go to your bosom: Knock there, and ask your heart what it doth know.","In black ink my love may still shine bright.","Love alters not with his brief hours and weeks, but bears it out even to the edge of doom.","See how she leans her cheek upon her hand. O, that I were a glove upon that hand that I might touch that cheek!","The course of true love never did run smooth.","Love sought is good, but given unsought, is better.","For which of my bad parts didst thou first fall in love with me?","Speak low, if you speak love.","Love comforteth like sunshine after rain.","Good night, good night! Parting is such sweet sorrow, that I shall say good night till it be morrow.","So long as men can breathe or eyes can see, so long lives this and this gives life to thee.","For you, in my respect, are all the world.","Love is merely a madness.","Love is not love which alters when it alteration finds.","How art thou out of breath when thou hast breath to say to me that thou art out of breath?","I wish my horse had the speed of your tongue.","Do you not know I am a woman? When I think, I must speak.","I had rather hear my dog bark at a crow, than a man swear he loves me.","'I can see that he's not in your good books,' said the messenger. 'No, and if he were I would burn my library.'","God has given you one face, and you make yourself another.","Misery acquaints a man with strange bedfellows.","He that loves to be flattered is worthy o' the flatterer.","Life is as tedious as twice-told tale, vexing the dull ear of a drowsy man.","Maids want nothing but husbands, and when they have them, they want everything.","O thou invisible spirit of wine, if thou hast no name to be known by, let us call thee devil.","Lord, what fools these mortals be!","I will praise any man that will praise me.","My pride fell with my fortunes.","Better a witty fool than a foolish wit.","Is it not strange that desire should so many years outlive performance?","I dote on his very absence.","There's many a man has more hair than wit.","Cowards die many times before their deaths; the valiant never taste of death but once.","A fool thinks himself to be wise, but a wise man knows himself to be a fool.","I am not bound to please thee with my answer."]; -const quotesjane=["The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.","Friendship is certainly the finest balm for the pangs of disappointed love","Give a loose rein to your fancy, indulge your imagination in every possible flight which the subject will afford","There is nothing I would not do for those who are really my friends. I have no notion of loving people by halves, it is not my nature.","I declare after all there is no enjoyment like reading! How much sooner one tires of anything than of a book!","In vain have I struggled. It will not do. My feelings will not be repressed. You must allow me to tell you how ardently I admire and love you.","It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.","I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation. It is too long ago. I was in the middle before I knew that I had begun.","Angry people are not always wise.","I hate to hear you talk about all women as if they were fine ladies instead of rational creatures. None of us want to be in calm waters all our lives.","Vanity and pride are different things, though the words are often used synonymously.","You pierce my soul. I am half agony, half hope. Tell me not that I am too late, that such precious feelings are gone for ever.","There is a stubbornness about me that never can bear to be frightened at the will of others. My courage always rises at every attempt to intimidate me.","What are men to rocks and mountains?","I could easily forgive his pride, if he had not mortified mine.","Ah! There is nothing like staying at home, for real comfort.","If I loved you less, I might be able to talk about it more.","I always deserve the best treatment because I never put up with any other.","My good opinion once lost is lost forever.","There could have been no two hearts so open, no tastes so similar, no feelings so in unison","Nothing ever fatigues me, but doing what I do not like.","I wish, as well as everybody else, to be perfectly happy; but, like everybody else, it must be in my own way.","There are people, who the more you do for them, the less they will do for themselves.","One half of the world cannot understand the pleasures of the other.","...when pain is over, the remembrance of it often becomes a pleasure.","Without music, life would be a blank to me.","You must be the best judge of your own happiness.","If adventures will not befall a young lady in her own village, she must seek them abroad.","I am only resolved to act in that manner, which will, in my own opinion, constitute my happiness, without reference to you, or to any person so wholly unconnected with me.","I am excessively diverted.","We have all a better guide in ourselves, if we would attend to it, than any other person can be.","A fondness for reading, properly directed, must be an education in itself.","Indeed, I am very sorry to be right in this instance. I would much rather have been merry than wise.","We do not suffer by accident.","Anne hoped she had outlived the age of blushing; but the age of emotion she certainly had not.","Do not let the behaviour of others destroy your inner peace.","There are as many forms of love as there are moments in time.","Indulge your imagination in every possible flight.","Obstinate, headstrong girl!","Why not seize the pleasure at once? -- How often is happiness destroyed by preparation, foolish preparation!","Is not general incivility the very essence of love?","She had a lively, playful disposition that delighted in anything ridiculous.","Now I must give one smirk, and then we may be rational again.","It is not what we think or feel that makes us who we are. It is what we do. Or fail to do...","Money can only give happiness where there is nothing else to give it.","I was so anxious to do what is right that I forgot to do what is right."]; -const quotesother=["The cure for anything is salt water, sweat, tears, or the salt sea.","Stuff your eyes with wonder. Live as if you'd drop dead in ten seconds. See the world.","It's opener, out there, in the wide, open air.","Oh, the places you'll go!","A little magic can take you a long way.","Most of the really exciting things we do in our lives scare us to death. They wouldn't be exciting if they didn't.","Somewhere inside all of us is the power to change the world.","if you have good thoughts they will shine out of your face like sunbeams and you will always look lovely.","I understand what you're saying, and your comments are valuable, but I'm gonna ignore your advice.","Meanings is not important, said the BFG. I cannot be right all the time. Quite often I is left instead of right.","It doesn't matter who you are or what you look like, so long as somebody loves you.","A little nonsense now and then is relished by the wisest men.","But in a solitary life, there are rare moments when another soul dips near yours, as stars once a year brush the earth. Such a constellation was he to me.","He showed me his scars, and in return he let me pretend that I had none.","Humbling women seems to me a chief pastime of poets. As if there can be no story unless we crawl and weep.","I thought: I cannot bear this world a moment longer. Then, child, make another.","Only that: we are here. This is what it means to swim in the tide, to walk the earth and feel it touch your feet. This is what it means to be alive.","I will not be like a bird bred in a cage, I thought, too dull to fly even when the door stands open.","The truth is, men make terrible pigs.","It was my first lesson. Beneath the smooth, familiar face of things is another that waits to tear the world in two.","As you set out for Ithaka, hope your road is a long one, Keep Ithaka always in your mind. Arriving there is what you're destined for. But don't hurry the journey.","It takes a great deal of bravery to stand up to our enemies, but just as much to stand up to our friends.","If you want to know what a man's like, take a good look at how he treats his inferiors, not his equals.","When in doubt, go to the library.","It is our choices, Harry, that show what we truly are, far more than our abilities.","Don't worry. You're just as sane as I am.","I have hated words and I have loved them, and I hope I have made them right.","Like most misery, it started with apparent happiness.","A DEFINITION NOT FOUND IN THE DICTIONARY: Not leaving: an act of trust and love, often deciphered by children","One was a book thief. The other stole the sky.","In his blue gardens men and girls came and went like moths among the whisperings and the champagne and the stars.","So we beat on, boats against the current, borne back ceaselessly into the past.","He looked at her the way all women want to be looked at by a man.","The loneliest moment in someone's life is when they are watching their whole world fall apart, and all they can do is stare blankly.","Whenever you feel like criticizing any one...just remember that all the people in this world haven't had the advantages that you've had.","In the midst of winter, I found there was, within me, an invincible summer. And that makes me happy.","And in me too the wave rises. It swells; it arches its back. I am aware once more of a new desire","Lock up your libraries if you like; but there is no gate, no lock, no bolt that you can set upon the freedom of my mind.","One cannot think well, love well, sleep well, if one has not dined well.","People who love to eat are always the best people","Why, sometimes I've believed as many as six impossible things before breakfast.","It's no use going back to yesterday, because I was a different person then.","To live is the rarest thing in the world. Most people exist, that is all.","We are all in the gutter, but some of us are looking at the stars.","The truth is rarely pure and never simple.","Never love anyone who treats you like you're ordinary.","Not all those who wander are lost","When spring came, even the false spring, there were no problems except where to be happiest.","I don't want comfort. (...) I want poetry. I want danger. I want freedom. I want goodness. I want sin.","Morning without you is a dwindled dawn.","I took a deep breath and listened to the old brag of my heart. I am, I am, I am.","Earth laughs in flowers","I felt my lungs inflate with the onrush of scener air, mountains, trees, people. I thought, This is what it is to be happy.","She never went astray, never made an error. I had been jealous then. Now I thought: what a burden. What an ugly weight upon your back.","Live in the sunshine, swim the sea, drink the wild air.","I felt once more how simple and frugal a thing is happiness: a glass of wine, a roast chestnut, a wretched little brazier, the sound of the sea. Nothing else.","I will clamber through the Clouds and exist.","Just living isn't enough, said the butterfly, one must have sunshine, freedom and a little flower.","It is a serious thing, to be alive, on this fresh morning, in this broken world","We loved with a love that was more than love","To young men contemplating a voyage I would say, go.","Encuéntrate y pon la serendipia del revés. Haz del mundo tu pista de baile.","I discover myself on the verge of a usual mistake","I too am not a bit tamed I too am untranslatable; I sound my barbaric yawp over the roofs of the world.","Do I contradict myself? Very well then, I contradict myself; I am large, I contain multitudes.","No need to hurry. No need to sparkle. No need to be anybody but oneself.","Procura guardar siempre por encima de tu vida un buen espacio de cielo.","El mundo hay que fabricárselo uno mismo, hay que crear peldanos que te suban, que te saquen del pozo. Hay que inventar la vida porque acaba siendo verdad","En vista de lo visto me desvisto, me desnudo a mí misma y me mantengo, me encanta este tener lo que no tengo","Lo mejor del olvido es el recuerdo","I suggest we learn to love ourselves before it's made illegal","To unpathed waters, undreamed shores.",]; -var skullleft={width:32,height:40,bpp:4,buffer:require("heatshrink").decompress(atob("AAcBrXf//sygOJz//AAfmB48eBwgAB4QPGv4PG/8QB4vvB49gBwkDBw//0APEhQPI6APEt4PI+AODgX/23P/eOB5MM/EA1/xMYoGBAAU/wBQBHAMDMgYPEh8wl35+YGBlxPHhOk7+T/RlF2BPF/kSTIRlDyAPDgomC//pxRPDqAPDhqWC36/Kn4fCeAv9TwmP//5HYXfB4XlB4ldqQKC+l5AYO0dosIwwPC+f25+CBwusmn6FYX1/9AgEwB4ku/G/q39//2EINt/IPEgS4B+VQ7nJOIYiBN4v9qMRidPXwcC5yfEAAOSk2eMgcvQIlOTgoAB/e/B4kB14PG/ihGgZSBAAfJxYPGKQQADYgYPFgNZ5vFif/54PIAAnPB4X6UYoAEpRFB/19+wOIgwtCzICB6oPHr4LB+4xC/APHg4LBtwiCyAwInn3BwX3MBVvBwP9L5QPDwIOKgG/BxsAxdQAoY="))}; -var skullright={width:32,height:40,bpp:4,buffer:require("heatshrink").decompress(atob("AAtJ53//uliAOIt3/AAf5CA8sBwgAB8IOFgIOG//3B4sGB4/+B4sKB4//mAPEhoPI0APEh4PI+wPK9G//lr/8gB4cfLYsf/UAx/AB4/+JQJGBmEI+ZPH8QGBn+f8UD+APDhZZF185/tKyAPDhJZEUoMh55PFgpPD1Ge/44CqC/K/aoC6CAE77uFD4RfEgFeBoX9IYWf//4aItLBgOXp4UCkvVB4sAlH8t8/B4VowAOFgcAgn/r4zC19D5QPEz/WDoNv//fsv7x6nCAAIcBNoWc5sFl7GBSAXskDAD+kxiMVO4JvD7/yLQfpsUpAoSfD7/736gFAAPkTorHBAAv6iAPF3GcBwn+mBsEXQYPEXgoPD/n/mNc7mVFoibB14PC/i3GAAVv6/vHIOkBxFdDoOZGIVgB4+PFoV/AYP1B48JDgXmAQNwGBAcC/9/4ZPJgaPB//2BxIABjAPOCAX7B5kAgu4BZI="))}; -var janeright={width:32,height:40,bpp:1,transparent:-1,palette:new Uint16Array([0,65535]),buffer:require("heatshrink").decompress(atob("AAM8AQMD/4DBg//4EAh//8EAj//+ADC/ALC/wDGDgIPBA4IDF/gjEAYIrDwADCG4ZDCv4LBgE/AYYXBAYIfBB4IDDI4IDFJYMAG4UBH4RnBAYIvBK4pPHNYYDBKYobBIYMDCwIDBCwIDBHQUDBwI"))}; -var janeleft={width:32,height:40,bpp:1,transparent:-1,palette:new Uint16Array([0,65535]),buffer:require("heatshrink").decompress(atob("gE8AYX/4EAgf/8EAg//+EAh///EAj4DCv4LCAYf/A4gPDAYM/AYQjDFYIDBgIDCDgIDDG4MB/wGBgP8AYMDAYUHAYYPCh4DGj4DCG4IDBH4OAAYRnBAYIvBLYpPFNYZTBAYJTFDYIYBEYIYBAYUPAYUBAYI="))}; -var bookleft={width:32,height:40,bpp:4,transparent:-1,palette:new Uint16Array([0,52857,2113,65535,4258,32,25388,29614,4226,6371,48631,16936,2145,8452,12678,52825]),buffer:require("heatshrink").decompress(atob("AH8iAC9VlFJ7vu9IMGlHd9e2s931WjigPGxWq1/xiIACtAPGsYMDAAMTygOFlsxB4sXhOCB4knBwsRn0I9gODku/B42ihviB4cpuI+G2EO6QPDlY+GjXQk3CB4efFw0XkHbHwlzB437gFkB4cuFw0R1MMuCcEBw0RuEGJwmxBw0T6FXTgmhB40agHuB4cmHw87gDOE6I+H/EE3htDTgw+BssOVodbFw8R8FbyQPCzQOHi+Aww+DsIPHixeENpER1sAuQPC40a1Wv/4ABTocNsoPC5eczue92721nu938EHJwcp2EAAAkFrMAo9SJ4fWoAQFgkA3wODAAO3u92tfu7KXBzCtEAANQgGE7vrIANms2yB4oAEktVlGJwQPKABBMFAH4AJA="))}; -var bookright={width:32,height:40,bpp:4,transparent:-1,palette:new Uint16Array([0,52857,2113,65535,4258,32,25388,29614,4226,6371,48631,16936,2145,8452,12678,52825]),buffer:require("heatshrink").decompress(atob("AH8iAB+e93dymCqoPJoM61V3u1r33dwQPGw0RAAUf/Wq1APGpMxCAYABmwOFlGQuIPFifSB4nOwHjB4sRuQPEl3QlQPG/dSB4ct8ELIA0XyQPDllihuhIA2yGAm9gRQGiP5B4lGgG/B403IAkH4GaB40T8RQEsEHB40RUQt1hpgGiMbB4nugBgGiMaUQmGgGzIA9iB4fLoGPIA8dWIngqxAHi5xDlO1hwfHie1EAdohChHiOpMAthB48WB4cngHaB45xEq3QUIf/AAP61WhthQDuEOu93u1r3fu9PZ5O8B4UlukAytQgAAEheSIAfrgFABwsEtrBEw2JQ4Od922s93vYOEkUrs1mHwO+7tIgEFB4sozGCqqpDACZIFAH4AJA"))}; +const locale = require("locale"); + +const quotesshakespeare = ["Be not afraid of greatness. Some are born great, some achieve greatness, and others have greatness thrust upon them.", "We know what we are, but know not what we may be.", "Sweet are the uses of adversity which, like the toad, ugly and venomous, wears yet a precious jewel in his head.", "Our doubts are traitors and make us lose the good we oft might win by fearing to attempt.", "Give every man thy ear, but few thy voice.", "Uneasy lies the head that wears the crown.", "How poor are they that have not patience! What wound did ever heal but by degrees?", "Nothing can come of nothing.", "How far that little candle throws its beams! So shines a good deed in a naughty world.", "What's done can't be undone.", "Though she be but little, she is fierce.", "No legacy is so rich as honesty.", "This above all; to thine own self be true.", "I wasted time, and now doth time waste me.", "The robbed that smiles, steals something from the thief.", "The devil can cite Scripture for his purpose.", "One touch of nature makes the whole world kin.", "What is past is prologue.", "Small cheer and great welcome makes a merry feast.", "Sweet mercy is nobility's true badge.", "'Tis not enough to help the feeble up, but to support them after.", "Neither a borrower nor a lender be.", "Ambition should be made of sterner stuff.", "I bear a charmed life.", "Heat not a furnace for your foe so hot that it do singe yourself.", "Talking isn't doing. It is a kind of good deed to say well; and yet words are not deeds.", "In time we hate that which we often fear.", "Modest doubt is called the beacon of the wise.", "With mirth and laughter let old wrinkles come.", "Boldness be my friend.", "Words without thoughts never to heaven go.", "Wisely, and slow. They stumble that run fast.", "Pleasure and action make the hours seem short.", "When words are scarce they are seldom spent in vain.", "Such as we are made of, such we be.", "And oftentimes excusing of a fault doth make the fault the worse by the excuse.", "Reputation is an idle and most false imposition; oft got without merit, and lost without deserving.", "To be, or not to be: that is the question.", "All the world's a stage, and all the men and women merely players. They have their exits and their entrances; and one man in his time plays many parts.", "All that glisters is not gold.", "Words are easy, like the wind; faithful friends are hard to find.", "The fault is not in our stars, but in ourselves.", "And this, our life, exempt from public haunt, finds tongues in trees, books in the running brooks, sermons in stones, and good in everything.", "Expectation is the root of all heartache.", "I like this place and could willingly waste my time in it.", "Better three hours too soon than a minute too late.", "Life's but a walking shadow, a poor player, that struts and frets his hour upon the stage, and then is heard no more; it is a tale told by an idiot, full of sound and fury, signifying nothing.", "My tongue will tell the anger of my heart, or else my heart concealing it will break.", "Brevity is the soul of wit.", "Give sorrow words; the grief that does not speak knits up o-er wrought heart and bids it break.", "Look like the innocent flower, but be the serpent under it.", "One may smile, and smile, be a villain.", "Conscience doth make cowards of us all.", "Let me be that I am and seek not to alter me.", "Et tu, Brute?", "O, beware, my lord, of jealousy; it is the green-ey'd monster, which doth mock the meat it feeds on.", "If we are true to ourselves, we can not be false to anyone.", "Be great in act, as you have been in thought.", "Suspicion always haunts the guilty mind.", "All things are ready, if our mind be so.", "Many a true word hath been spoken in jest.", "For sweetest things turn sourest by their deeds; lillies that fester smell far worse than weeds.", "The Devil hath power to assume a pleasing shape.", "Thought is free.", "April hath put a spirit of youth in everything.", "Summer's lease hath all too short a date.", "Our bodies are our gardens to the which our wills are gardeners.", "The tempter or the tempted, who sins most?", "Men should be what they seem.", "He jests at scars that never felt a wound.", "I would not wish any companion in the world but you.", "Self-love, my liege, is not so vile a sin, as self-neglecting.", "Doubt thou the stars are fire, doubt that the sun doth move. Doubt truth to be a liar, but never doubt I love.", "I am one who loved not wisely but too well.", "A young woman in love always looks like patience on a monument smiling at grief.", "My bounty is as boundless as the sea, my love as deep; the more I give to thee, the more I have, for both are infinite.", "They do not love that do not show their love.", "I love you with so much of my heart that none is left to protest.", "Love is heavy and light, bright and dark, hot and cold, sick and healthy, asleep and awake.", "Shall I compare thee to a summer's day? Thou art more lovely and more temperate.", "Love all, trust a few, do wrong to none.", "Kindness in women, not their beauteous looks, shall win my love.", "Love looks not with the eyes, but with the mind, and therefore is winged Cupid painted blind.", "Do not swear by the moon, for she changes constantly. Then your love would also change.", "If music be the food of love, play on.", "Love is too young to know what conscience is.", "Did my heart love till now? Forswear it, sight! For I ne'er saw true beauty till this night.", "Don't waste your love on somebody, who doesn't value it.", "And yet, to say the truth, reason and love keep little company together nowadays.", "Love is a smoke made with the fume of sighs.", "Go to your bosom: Knock there, and ask your heart what it doth know.", "In black ink my love may still shine bright.", "Love alters not with his brief hours and weeks, but bears it out even to the edge of doom.", "See how she leans her cheek upon her hand. O, that I were a glove upon that hand that I might touch that cheek!", "The course of true love never did run smooth.", "Love sought is good, but given unsought, is better.", "For which of my bad parts didst thou first fall in love with me?", "Speak low, if you speak love.", "Love comforteth like sunshine after rain.", "Good night, good night! Parting is such sweet sorrow, that I shall say good night till it be morrow.", "So long as men can breathe or eyes can see, so long lives this and this gives life to thee.", "For you, in my respect, are all the world.", "Love is merely a madness.", "Love is not love which alters when it alteration finds.", "How art thou out of breath when thou hast breath to say to me that thou art out of breath?", "I wish my horse had the speed of your tongue.", "Do you not know I am a woman? When I think, I must speak.", "I had rather hear my dog bark at a crow, than a man swear he loves me.", "'I can see that he's not in your good books,' said the messenger. 'No, and if he were I would burn my library.'", "God has given you one face, and you make yourself another.", "Misery acquaints a man with strange bedfellows.", "He that loves to be flattered is worthy o' the flatterer.", "Life is as tedious as twice-told tale, vexing the dull ear of a drowsy man.", "Maids want nothing but husbands, and when they have them, they want everything.", "O thou invisible spirit of wine, if thou hast no name to be known by, let us call thee devil.", "Lord, what fools these mortals be!", "I will praise any man that will praise me.", "My pride fell with my fortunes.", "Better a witty fool than a foolish wit.", "Is it not strange that desire should so many years outlive performance?", "I dote on his very absence.", "There's many a man has more hair than wit.", "Cowards die many times before their deaths; the valiant never taste of death but once.", "A fool thinks himself to be wise, but a wise man knows himself to be a fool.", "I am not bound to please thee with my answer."]; + +const quotesjane = ["The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.", "Friendship is certainly the finest balm for the pangs of disappointed love", "Give a loose rein to your fancy, indulge your imagination in every possible flight which the subject will afford", "There is nothing I would not do for those who are really my friends. I have no notion of loving people by halves, it is not my nature.", "I declare after all there is no enjoyment like reading! How much sooner one tires of anything than of a book!", "In vain have I struggled. It will not do. My feelings will not be repressed. You must allow me to tell you how ardently I admire and love you.", "It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.", "I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation. It is too long ago. I was in the middle before I knew that I had begun.", "Angry people are not always wise.", "I hate to hear you talk about all women as if they were fine ladies instead of rational creatures. None of us want to be in calm waters all our lives.", "Vanity and pride are different things, though the words are often used synonymously.", "You pierce my soul. I am half agony, half hope. Tell me not that I am too late, that such precious feelings are gone for ever.", "There is a stubbornness about me that never can bear to be frightened at the will of others. My courage always rises at every attempt to intimidate me.", "What are men to rocks and mountains?", "I could easily forgive his pride, if he had not mortified mine.", "Ah! There is nothing like staying at home, for real comfort.", "If I loved you less, I might be able to talk about it more.", "I always deserve the best treatment because I never put up with any other.", "My good opinion once lost is lost forever.", "There could have been no two hearts so open, no tastes so similar, no feelings so in unison", "Nothing ever fatigues me, but doing what I do not like.", "I wish, as well as everybody else, to be perfectly happy; but, like everybody else, it must be in my own way.", "There are people, who the more you do for them, the less they will do for themselves.", "One half of the world cannot understand the pleasures of the other.", "...when pain is over, the remembrance of it often becomes a pleasure.", "Without music, life would be a blank to me.", "You must be the best judge of your own happiness.", "If adventures will not befall a young lady in her own village, she must seek them abroad.", "I am only resolved to act in that manner, which will, in my own opinion, constitute my happiness, without reference to you, or to any person so wholly unconnected with me.", "I am excessively diverted.", "We have all a better guide in ourselves, if we would attend to it, than any other person can be.", "A fondness for reading, properly directed, must be an education in itself.", "Indeed, I am very sorry to be right in this instance. I would much rather have been merry than wise.", "We do not suffer by accident.", "Anne hoped she had outlived the age of blushing; but the age of emotion she certainly had not.", "Do not let the behaviour of others destroy your inner peace.", "There are as many forms of love as there are moments in time.", "Indulge your imagination in every possible flight.", "Obstinate, headstrong girl!", "Why not seize the pleasure at once? -- How often is happiness destroyed by preparation, foolish preparation!", "Is not general incivility the very essence of love?", "She had a lively, playful disposition that delighted in anything ridiculous.", "Now I must give one smirk, and then we may be rational again.", "It is not what we think or feel that makes us who we are. It is what we do. Or fail to do...", "Money can only give happiness where there is nothing else to give it.", "I was so anxious to do what is right that I forgot to do what is right."]; + +const quotesother = ["The cure for anything is salt water, sweat, tears, or the salt sea.", "Stuff your eyes with wonder. Live as if you'd drop dead in ten seconds. See the world.", "It's opener, out there, in the wide, open air.", "Oh, the places you'll go!", "A little magic can take you a long way.", "Most of the really exciting things we do in our lives scare us to death. They wouldn't be exciting if they didn't.", "Somewhere inside all of us is the power to change the world.", "if you have good thoughts they will shine out of your face like sunbeams and you will always look lovely.", "I understand what you're saying, and your comments are valuable, but I'm gonna ignore your advice.", "Meanings is not important, said the BFG. I cannot be right all the time. Quite often I is left instead of right.", "It doesn't matter who you are or what you look like, so long as somebody loves you.", "A little nonsense now and then is relished by the wisest men.", "But in a solitary life, there are rare moments when another soul dips near yours, as stars once a year brush the earth. Such a constellation was he to me.", "He showed me his scars, and in return he let me pretend that I had none.", "Humbling women seems to me a chief pastime of poets. As if there can be no story unless we crawl and weep.", "I thought: I cannot bear this world a moment longer. Then, child, make another.", "Only that: we are here. This is what it means to swim in the tide, to walk the earth and feel it touch your feet. This is what it means to be alive.", "I will not be like a bird bred in a cage, I thought, too dull to fly even when the door stands open.", "The truth is, men make terrible pigs.", "It was my first lesson. Beneath the smooth, familiar face of things is another that waits to tear the world in two.", "As you set out for Ithaka, hope your road is a long one, Keep Ithaka always in your mind. Arriving there is what you're destined for. But don't hurry the journey.", "It takes a great deal of bravery to stand up to our enemies, but just as much to stand up to our friends.", "If you want to know what a man's like, take a good look at how he treats his inferiors, not his equals.", "When in doubt, go to the library.", "It is our choices, Harry, that show what we truly are, far more than our abilities.", "Don't worry. You're just as sane as I am.", "I have hated words and I have loved them, and I hope I have made them right.", "Like most misery, it started with apparent happiness.", "A DEFINITION NOT FOUND IN THE DICTIONARY: Not leaving: an act of trust and love, often deciphered by children", "One was a book thief. The other stole the sky.", "In his blue gardens men and girls came and went like moths among the whisperings and the champagne and the stars.", "So we beat on, boats against the current, borne back ceaselessly into the past.", "He looked at her the way all women want to be looked at by a man.", "The loneliest moment in someone's life is when they are watching their whole world fall apart, and all they can do is stare blankly.", "Whenever you feel like criticizing any one...just remember that all the people in this world haven't had the advantages that you've had.", "In the midst of winter, I found there was, within me, an invincible summer. And that makes me happy.", "And in me too the wave rises. It swells; it arches its back. I am aware once more of a new desire", "Lock up your libraries if you like; but there is no gate, no lock, no bolt that you can set upon the freedom of my mind.", "One cannot think well, love well, sleep well, if one has not dined well.", "People who love to eat are always the best people", "Why, sometimes I've believed as many as six impossible things before breakfast.", "It's no use going back to yesterday, because I was a different person then.", "To live is the rarest thing in the world. Most people exist, that is all.", "We are all in the gutter, but some of us are looking at the stars.", "The truth is rarely pure and never simple.", "Never love anyone who treats you like you're ordinary.", "Not all those who wander are lost", "When spring came, even the false spring, there were no problems except where to be happiest.", "I don't want comfort. (...) I want poetry. I want danger. I want freedom. I want goodness. I want sin.", "Morning without you is a dwindled dawn.", "I took a deep breath and listened to the old brag of my heart. I am, I am, I am.", "Earth laughs in flowers", "I felt my lungs inflate with the onrush of scener air, mountains, trees, people. I thought, This is what it is to be happy.", "She never went astray, never made an error. I had been jealous then. Now I thought: what a burden. What an ugly weight upon your back.", "Live in the sunshine, swim the sea, drink the wild air.", "I felt once more how simple and frugal a thing is happiness: a glass of wine, a roast chestnut, a wretched little brazier, the sound of the sea. Nothing else.", "I will clamber through the Clouds and exist.", "Just living isn't enough, said the butterfly, one must have sunshine, freedom and a little flower.", "It is a serious thing, to be alive, on this fresh morning, in this broken world", "We loved with a love that was more than love", "To young men contemplating a voyage I would say, go.", "Encuéntrate y pon la serendipia del revés. Haz del mundo tu pista de baile.", "I discover myself on the verge of a usual mistake", "I too am not a bit tamed I too am untranslatable; I sound my barbaric yawp over the roofs of the world.", "Do I contradict myself? Very well then, I contradict myself; I am large, I contain multitudes.", "No need to hurry. No need to sparkle. No need to be anybody but oneself.", "Procura guardar siempre por encima de tu vida un buen espacio de cielo.", "El mundo hay que fabricárselo uno mismo, hay que crear peldanos que te suban, que te saquen del pozo. Hay que inventar la vida porque acaba siendo verdad", "En vista de lo visto me desvisto, me desnudo a mí misma y me mantengo, me encanta este tener lo que no tengo", "Lo mejor del olvido es el recuerdo", "I suggest we learn to love ourselves before it's made illegal", "To unpathed waters, undreamed shores.", ]; + +var skullleft = { + width: 32, + height: 40, + bpp: 4, + buffer: require("heatshrink").decompress(atob("AAcBrXf//sygOJz//AAfmB48eBwgAB4QPGv4PG/8QB4vvB49gBwkDBw//0APEhQPI6APEt4PI+AODgX/23P/eOB5MM/EA1/xMYoGBAAU/wBQBHAMDMgYPEh8wl35+YGBlxPHhOk7+T/RlF2BPF/kSTIRlDyAPDgomC//pxRPDqAPDhqWC36/Kn4fCeAv9TwmP//5HYXfB4XlB4ldqQKC+l5AYO0dosIwwPC+f25+CBwusmn6FYX1/9AgEwB4ku/G/q39//2EINt/IPEgS4B+VQ7nJOIYiBN4v9qMRidPXwcC5yfEAAOSk2eMgcvQIlOTgoAB/e/B4kB14PG/ihGgZSBAAfJxYPGKQQADYgYPFgNZ5vFif/54PIAAnPB4X6UYoAEpRFB/19+wOIgwtCzICB6oPHr4LB+4xC/APHg4LBtwiCyAwInn3BwX3MBVvBwP9L5QPDwIOKgG/BxsAxdQAoY=")) +}; +var skullright = { + width: 32, + height: 40, + bpp: 4, + buffer: require("heatshrink").decompress(atob("AAtJ53//uliAOIt3/AAf5CA8sBwgAB8IOFgIOG//3B4sGB4/+B4sKB4//mAPEhoPI0APEh4PI+wPK9G//lr/8gB4cfLYsf/UAx/AB4/+JQJGBmEI+ZPH8QGBn+f8UD+APDhZZF185/tKyAPDhJZEUoMh55PFgpPD1Ge/44CqC/K/aoC6CAE77uFD4RfEgFeBoX9IYWf//4aItLBgOXp4UCkvVB4sAlH8t8/B4VowAOFgcAgn/r4zC19D5QPEz/WDoNv//fsv7x6nCAAIcBNoWc5sFl7GBSAXskDAD+kxiMVO4JvD7/yLQfpsUpAoSfD7/736gFAAPkTorHBAAv6iAPF3GcBwn+mBsEXQYPEXgoPD/n/mNc7mVFoibB14PC/i3GAAVv6/vHIOkBxFdDoOZGIVgB4+PFoV/AYP1B48JDgXmAQNwGBAcC/9/4ZPJgaPB//2BxIABjAPOCAX7B5kAgu4BZI=")) +}; +var janeright = { + width: 32, + height: 40, + bpp: 1, + transparent: -1, + palette: new Uint16Array([0, 65535]), + buffer: require("heatshrink").decompress(atob("AAM8AQMD/4DBg//4EAh//8EAj//+ADC/ALC/wDGDgIPBA4IDF/gjEAYIrDwADCG4ZDCv4LBgE/AYYXBAYIfBB4IDDI4IDFJYMAG4UBH4RnBAYIvBK4pPHNYYDBKYobBIYMDCwIDBCwIDBHQUDBwI")) +}; +var janeleft = { + width: 32, + height: 40, + bpp: 1, + transparent: -1, + palette: new Uint16Array([0, 65535]), + buffer: require("heatshrink").decompress(atob("gE8AYX/4EAgf/8EAg//+EAh///EAj4DCv4LCAYf/A4gPDAYM/AYQjDFYIDBgIDCDgIDDG4MB/wGBgP8AYMDAYUHAYYPCh4DGj4DCG4IDBH4OAAYRnBAYIvBLYpPFNYZTBAYJTFDYIYBEYIYBAYUPAYUBAYI=")) +}; +var bookleft = { + width: 32, + height: 40, + bpp: 4, + transparent: -1, + palette: new Uint16Array([0, 52857, 2113, 65535, 4258, 32, 25388, 29614, 4226, 6371, 48631, 16936, 2145, 8452, 12678, 52825]), + buffer: require("heatshrink").decompress(atob("AH8iAC9VlFJ7vu9IMGlHd9e2s931WjigPGxWq1/xiIACtAPGsYMDAAMTygOFlsxB4sXhOCB4knBwsRn0I9gODku/B42ihviB4cpuI+G2EO6QPDlY+GjXQk3CB4efFw0XkHbHwlzB437gFkB4cuFw0R1MMuCcEBw0RuEGJwmxBw0T6FXTgmhB40agHuB4cmHw87gDOE6I+H/EE3htDTgw+BssOVodbFw8R8FbyQPCzQOHi+Aww+DsIPHixeENpER1sAuQPC40a1Wv/4ABTocNsoPC5eczue92721nu938EHJwcp2EAAAkFrMAo9SJ4fWoAQFgkA3wODAAO3u92tfu7KXBzCtEAANQgGE7vrIANms2yB4oAEktVlGJwQPKABBMFAH4AJA=")) +}; +var bookright = { + width: 32, + height: 40, + bpp: 4, + transparent: -1, + palette: new Uint16Array([0, 52857, 2113, 65535, 4258, 32, 25388, 29614, 4226, 6371, 48631, 16936, 2145, 8452, 12678, 52825]), + buffer: require("heatshrink").decompress(atob("AH8iAB+e93dymCqoPJoM61V3u1r33dwQPGw0RAAUf/Wq1APGpMxCAYABmwOFlGQuIPFifSB4nOwHjB4sRuQPEl3QlQPG/dSB4ct8ELIA0XyQPDllihuhIA2yGAm9gRQGiP5B4lGgG/B403IAkH4GaB40T8RQEsEHB40RUQt1hpgGiMbB4nugBgGiMaUQmGgGzIA9iB4fLoGPIA8dWIngqxAHi5xDlO1hwfHie1EAdohChHiOpMAthB48WB4cngHaB45xEq3QUIf/AAP61WhthQDuEOu93u1r3fu9PZ5O8B4UlukAytQgAAEheSIAfrgFABwsEtrBEw2JQ4Od922s93vYOEkUrs1mHwO+7tIgEFB4sozGCqqpDACZIFAH4AJA")) +}; + + let hour; let minute; let date; let timer; -const watchtop=55; -const hoursize=32; -const datesize=14; +const watchtop = 55; +const hoursize = 32; +const datesize = 14; let qnumber; let quotehour; let qtype; let quotes; -let imageleft=bookleft; -let imageright=bookright; -function draw(){const d=new Date();const newHour=('0'+d.getHours()).substr(-2);const newMinute=('0'+d.getMinutes()).substr(-2);const newDate=locale.date(d).trim();g.setFontAlign(0,0,0);if(newHour !== hour){g.setFont("Vector",hoursize);g.setColor(0x0000);g.drawString(hour,60,watchtop);g.setColor(0xFFFF);g.drawString(newHour,60,watchtop);hour=newHour;drawQuote();}g.setFontAlign(0,0,0);if(newMinute !== minute){g.setFont("Vector",hoursize);g.setColor(0x0000);g.drawString(minute,120,watchtop);g.setColor(0xFFFF);g.drawString(newMinute,120,watchtop);minute=newMinute;}g.setFontAlign(0,0,0);if(newDate !== date){g.setFont("Vector",datesize);g.setColor(0x0000);g.drawString(date,88,168);g.setColor(0xFFFF);g.drawString(newDate,88,168);date=newDate;}} -function drawStrCenter(str,colour){g.setColor(colour);g.setFont("Vector",13);str="\""+str+"\"";maxLength=24;var ta=[];let index=0;let linestart=0;while(indexmaxLength){ta.push(str.substring(linestart,index));ta.push(str.substring(index,str.length));}else ta.push(str.substring(linestart,str.length));break;}else if(((newIndex-linestart)>maxLength)){ta.push(str.substring(linestart,index));linestart=index;}else index=newIndex+1;}y=110-ta.length*5;ta.forEach((e)=>{g.setFontAlign(0,-1).drawString(e.trim(),88,y);y += 12;});} -function drawQuote(){if(qnumber == undefined || quotehour != hour){if(qnumber != undefined)drawStrCenter(quotes[qnumber],0x0000);qtype=Math.floor(Math.random()*3);if(qtype === 0){quotes=quotesshakespeare;imageleft=skullleft;imageright=skullright;}if(qtype === 1){quotes=quotesjane;imageleft=janeleft;imageright=janeright;}if(qtype === 2){quotes=quotesother;imageleft=bookleft;imageright=bookright;}drawImages();qnumber=Math.floor(Math.random()*quotes.length);quotehour=hour;}drawStrCenter(quotes[qnumber],0xFFFF);} -function drawImages(){g.drawImage(imageleft,0,30);g.drawImage(imageright,142,30);} -function startDrawing(){hour='';minute='';date='';g.setColor(0X0000);g.fillRect(0,0,176,176);g.setFontAlign(0,0,0);g.setFont("Vector",hoursize);g.setColor(0xFFFF);g.drawString(":",88,watchtop);drawImages();console.log("drawing image");Bangle.drawWidgets();draw();timer=setInterval(draw,1000);} -function stopDrawing(){if(timer){clearInterval(timer);timer=undefined;}} +let imageleft = bookleft; +let imageright = bookright; + +function draw() { + const d = new Date(); + const newHour = ('0' + d.getHours()).substr(-2); + const newMinute = ('0' + d.getMinutes()).substr(-2); + const newDate = locale.date(d).trim(); + g.setFontAlign(0, 0, 0); + if (newHour !== hour) { + g.setFont("Vector", hoursize); + g.setColor(0x0000); + g.drawString(hour, 60, watchtop); + g.setColor(0xFFFF); + g.drawString(newHour, 60, watchtop); + hour = newHour; + drawQuote(); + } + g.setFontAlign(0, 0, 0); + if (newMinute !== minute) { + g.setFont("Vector", hoursize); + g.setColor(0x0000); + g.drawString(minute, 120, watchtop); + g.setColor(0xFFFF); + g.drawString(newMinute, 120, watchtop); + minute = newMinute; + } + g.setFontAlign(0, 0, 0); + if (newDate !== date) { + g.setFont("Vector", datesize); + g.setColor(0x0000); + g.drawString(date, 88, 168); + g.setColor(0xFFFF); + g.drawString(newDate, 88, 168); + date = newDate; + } +} + +function drawStrCenter(str, colour) { + g.setColor(colour); + g.setFont("Vector", 13); + str = "\"" + str + "\""; + maxLength = 24; + var ta = []; + let index = 0; + let linestart = 0; + while (index < str.length) { + newIndex = str.indexOf(" ", index); + if (newIndex == -1) { + if ((str.length - linestart) > maxLength) { + ta.push(str.substring(linestart, index)); + ta.push(str.substring(index, str.length)); + } else ta.push(str.substring(linestart, str.length)); + break; + } else if (((newIndex - linestart) > maxLength)) { + ta.push(str.substring(linestart, index)); + linestart = index; + } else index = newIndex + 1; + } + y = 110 - ta.length * 5; + ta.forEach((e) => { + g.setFontAlign(0, -1).drawString(e.trim(), 88, y); + y += 12; + }); +} + +function drawQuote() { + if (qnumber == undefined || quotehour != hour) { + if (qnumber != undefined) drawStrCenter(quotes[qnumber], 0x0000); + qtype = Math.floor(Math.random() * 3); + if (qtype === 0) { + quotes = quotesshakespeare; + imageleft = skullleft; + imageright = skullright; + } + if (qtype === 1) { + quotes = quotesjane; + imageleft = janeleft; + imageright = janeright; + } + if (qtype === 2) { + quotes = quotesother; + imageleft = bookleft; + imageright = bookright; + } + drawImages(); + qnumber = Math.floor(Math.random() * quotes.length); + quotehour = hour; + } + drawStrCenter(quotes[qnumber], 0xFFFF); +} + +function drawImages() { + g.drawImage(imageleft, 0, 30); + g.drawImage(imageright, 142, 30); +} + +function startDrawing() { + hour = ''; + minute = ''; + date = ''; + g.setColor(0X0000); + g.fillRect(0, 0, 176, 176); + g.setFontAlign(0, 0, 0); + g.setFont("Vector", hoursize); + g.setColor(0xFFFF); + g.drawString(":", 88, watchtop); + drawImages(); + console.log("drawing image"); + Bangle.drawWidgets(); + draw(); + timer = setInterval(draw, 1000); +} + +function stopDrawing() { + if (timer) { + clearInterval(timer); + timer = undefined; + } +} g.clear(); -Bangle.on('lcdPower',(on)=>{stopDrawing();if(on){startDrawing();}}); +Bangle.on('lcdPower', (on) => { + stopDrawing(); + if (on) { + startDrawing(); + } +}); + +Bangle.setUI("clock"); Bangle.loadWidgets(); -startDrawing(); -Bangle.setUI("clock"); \ No newline at end of file +startDrawing(); \ No newline at end of file From a57de7766e55720fe0fb7c1f4ca0764cefa9b852 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Tue, 23 Jan 2024 20:28:39 +0100 Subject: [PATCH 49/70] tetris: Save/Restore game state --- apps/tetris/ChangeLog | 1 + apps/tetris/metadata.json | 5 +- apps/tetris/tetris.app.js | 220 +++++++++++++++++++++++--------------- 3 files changed, 136 insertions(+), 90 deletions(-) diff --git a/apps/tetris/ChangeLog b/apps/tetris/ChangeLog index 158cccc5b..be909ae52 100644 --- a/apps/tetris/ChangeLog +++ b/apps/tetris/ChangeLog @@ -3,3 +3,4 @@ 0.03: Implement mode and level selection screens. 0.04: Bring back old controls as "swipe" in menu, exit with button press 0.10: Major overhaul: added score, levels, bugfixes and misc, inspired by NES tetris +0.11: Save/Restore game state diff --git a/apps/tetris/metadata.json b/apps/tetris/metadata.json index 8b73ffb43..c82bb59f8 100644 --- a/apps/tetris/metadata.json +++ b/apps/tetris/metadata.json @@ -1,7 +1,7 @@ { "id": "tetris", "name": "Tetris", "shortName":"Tetris", - "version":"0.10", + "version":"0.11", "description": "Tetris", "icon": "tetris.png", "readme": "README.md", @@ -12,5 +12,8 @@ "storage": [ {"name":"tetris.app.js","url":"tetris.app.js"}, {"name":"tetris.img","url":"app-icon.js","evaluate":true} + ], + "data": [ + {"name":"tetris.json"} ] } diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index d90627bde..d3501a9f6 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -36,26 +36,44 @@ const tiles = [ const ox = 176/2 - 5*8; const oy = 8; +const FILE = "tetris.json"; + +const settings = Object.assign({ /* 0 .. simulated arrows 1 .. drag piece 2 .. accelerometer. 12 lines record. 3 .. altimeter */ -var control = 0, level = 0, lines = 0, score = 0; + control: 0, + level: 0, + lines: 0, + score: 0, + pf: undefined, + ctn: Math.floor(Math.random()*7), // current tile number + ntn: Math.floor(Math.random()*7), // next tile number + ntr: Math.floor(Math.random()*4), // next tile rotation + dropInterval: undefined, +}, require('Storage').readJSON(FILE, true) || {}); + var alt_start = -9999; /* For altimeter control */ /* 0 .. menu 1 .. game 2 .. game over */ var state = 0; -var pf; +var px=4, py=0; + +function writeSettings() { + require('Storage').writeJSON(FILE, settings); +} + function initGame() { - pf = Array(23).fill().map(()=>Array(12).fill(0)); // field is really 10x20, but adding a border for collision checks - pf[20].fill(1); - pf[21].fill(1); - pf[22].fill(1); - pf.forEach((x,i) => { pf[i][0] = 1; pf[i][11] = 1; }); + settings.pf = Array(23).fill().map(()=>Array(12).fill(0)); // field is really 10x20, but adding a border for collision checks + settings.pf[20].fill(1); + settings.pf[21].fill(1); + settings.pf[22].fill(1); + settings.pf.forEach((x,i) => { settings.pf[i][0] = 1; settings.pf[i][11] = 1; }); } function rotateTile(t, r) { @@ -71,6 +89,9 @@ function rotateTile(t, r) { return nt; } +var time = Date.now(); +var ct = rotateTile(tiles[settings.ctn], Math.floor(Math.random()*4)); // current tile (rotated) + function drawBoundingBox() { g.setBgColor(0, 0, 0).clear().setColor(1, 1, 1); g.theme.bg = 0; @@ -92,31 +113,23 @@ function drawTile(tile, n, x, y, qClear) { function showNext(n, r) { var nt = rotateTile(tiles[n], r); g.setColor(0).fillRect(176-33, 40, 176-33+33, 82); - drawTile(nt, ntn, 176-33, 40); + drawTile(nt, settings.ntn, 176-33, 40); } -var time = Date.now(); -var px=4, py=0; -var ctn = Math.floor(Math.random()*7); // current tile number -var ntn = Math.floor(Math.random()*7); // next tile number -var ntr = Math.floor(Math.random()*4); // next tile rotation -var ct = rotateTile(tiles[ctn], Math.floor(Math.random()*4)); // current tile (rotated) -var dropInterval; - function calculateSpeed() { let step = 500; - if (level <= 6) // 200-500ms - step = step - 50*level; - else if (level <= 13) { // 25-175ms + if (settings.level <= 6) // 200-500ms + step = step - 50*settings.level; + else if (settings.level <= 13) { // 25-175ms step = 200; - step = step - 25*(level - 6); + step = step - 25*(settings.level - 6); } else { step = 20; // usually limited by the hardware // levels 15+ are programmed to go faster by skipping lines } - print(`level ${level}: drop interval ${step}ms`); - if (control == 3) + print(`level ${settings.level}: drop interval ${step}ms`); + if (settings.control == 3) step = step*2; dropInterval = step; } @@ -124,7 +137,7 @@ function calculateSpeed() { function redrawPF(ly) { for (y=0; y<=ly; ++y) for (x=1; x<11; ++x) { - c = pf[y][x]; + c = settings.pf[y][x]; if (c>0) g.setColor(tcols[c-1].r, tcols[c-1].g, tcols[c-1].b).drawImage(block, ox+(x-1)*8, oy+y*8); else g.setColor(0, 0, 0).fillRect(ox+(x-1)*8, oy+y*8, ox+x*8-1, oy+(y+1)*8-1); } @@ -135,19 +148,19 @@ function gameOver() { g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("Vector",22) .drawString("Game Over", 176/2, 76); // this cannot allow changing game controls because it would set up duplicate events - E.showAlert("Game Over").then(startGame, print); - lines = 0; - score = 0; + E.showAlert("Game Over").then(newGame, print); + settings.lines = 0; + settings.score = 0; } function redrawStats(onlyScore) { g.setColor(0).fillRect(5, 30, 41, 60) - .setColor(1, 1, 1).drawString(score.toString(), 22, 50); + .setColor(1, 1, 1).drawString(settings.score.toString(), 22, 50); if (!onlyScore) { g.setColor(0).fillRect(5, 80, 41, 110) - .setColor(1, 1, 1).drawString(level.toString(), 22, 100) + .setColor(1, 1, 1).drawString(settings.level.toString(), 22, 100) .setColor(0).fillRect(5, 130, 41, 160) - .setColor(1, 1, 1).drawString(lines.toString(), 22, 150); + .setColor(1, 1, 1).drawString(settings.lines.toString(), 22, 150); } } @@ -155,41 +168,41 @@ function insertAndCheck() { // stop pieces from falling into each other for (let y=0; y0) pf[py+y][px+x+1] = ctn+1; + if (ct[y][x]>0) settings.pf[py+y][px+x+1] = settings.ctn+1; let clearCount = 0; let linesToClear = []; let yReal = 19; // the y for display purposes // check for full lines for (let y=19; y>0; y--) { var qFull = true; - for (let x=1; x<11; ++x) qFull &= pf[y][x]>0; + for (let x=1; x<11; ++x) qFull &= settings.pf[y][x]>0; if (qFull) { clearCount++; linesToClear.push([y, yReal]); print(`linesToClear.push(${y})`); // clear the line, but do not display it yet - for (ny=y; ny>0; ny--) pf[ny] = JSON.parse(JSON.stringify(pf[ny-1])); + for (ny=y; ny>0; ny--) settings.pf[ny] = JSON.parse(JSON.stringify(settings.pf[ny-1])); y++; } yReal--; } if (clearCount) { - lines += clearCount; - let effectiveLevel = Math.max(level, 1); + settings.lines += clearCount; + let effectiveLevel = Math.max(settings.level, 1); if (clearCount == 1) { // single - score += 100 * effectiveLevel; + settings.score += 100 * effectiveLevel; Bangle.buzz(80, 0.5); } else if (clearCount == 2) { // double - score += 300 * effectiveLevel; + settings.score += 300 * effectiveLevel; Bangle.buzz(80); } else if (clearCount == 3) { // triple - score += 500 * effectiveLevel; + settings.score += 500 * effectiveLevel; Bangle.buzz(200); } else if (clearCount >= 4) { // tetris - score += 800 * effectiveLevel; + settings.score += 800 * effectiveLevel; Bangle.buzz(500); } // the score will not be shown yet because redrawStats was not called @@ -213,19 +226,19 @@ function insertAndCheck() { for (let line of linesToClear) { redrawPF(line[0]); } - if (lines != 0 && lines % 10 == 0) { - level++; + if (settings.lines != 0 && settings.lines % 10 == 0) { + settings.level++; calculateSpeed(); } redrawStats(); } // spawn new tile px = 4; py = 0; - ctn = ntn; - ntn = Math.floor(Math.random()*7); - ct = rotateTile(tiles[ctn], ntr); - ntr = Math.floor(Math.random()*4); - showNext(ntn, ntr); + settings.ctn = settings.ntn; + settings.ntn = Math.floor(Math.random()*7); + ct = rotateTile(tiles[settings.ctn], settings.ntr); + settings.ntr = Math.floor(Math.random()*4); + showNext(settings.ntn, settings.ntr); if (!moveOk(ct, 0, 0)) { gameOver(); } @@ -235,18 +248,29 @@ function moveOk(t, dx, dy) { var ok = true; for (y=0; y 0) ok = false; + if (t[y][x]*settings.pf[py+dy+y][px+dx+x+1] > 0) ok = false; return ok; } function pauseGame() { - print("Paused"); + //print("Paused"); state = 3; + E.showMenu({ + "" : { "title" : /*LANG*/"Pause menu" }, + "< Back" : () => resumeGame(), + /*LANG*/"Exit" : () => { + writeSettings(); + load(); + }, + /*LANG*/"New game": () => newGame(), + }); } function resumeGame() { - print("Resumed"); + //print("Resumed"); state = 1; + drawGame(); + redrawPF(19); } function gameStep() { @@ -254,38 +278,38 @@ function gameStep() { return; if (Date.now()-time > dropInterval) { // drop one step time = Date.now(); - if (level >= 15 && moveOk(ct, 0, 2)) { + if (settings.level >= 15 && moveOk(ct, 0, 2)) { // at level 15, pieces drop twile as quickly - drawTile(ct, ctn, ox+px*8, oy+py*8, true); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, true); py += 2; } else if (moveOk(ct, 0, 1)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, true); py++; } else { // reached the bottom - insertAndCheck(ct, ctn, px, py); + insertAndCheck(ct, settings.ctn, px, py); } - drawTile(ct, ctn, ox+px*8, oy+py*8, false); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, false); } } function rotate() { t = rotateTile(ct, 3); if (moveOk(t, 0, 0)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, true); ct = t; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, false); } } function move(x, y) { r = moveOk(ct, x, y); if (r) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, true); px += x; py += y; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, false); } return r; } @@ -299,42 +323,40 @@ function linear(x) { move(1, 0); } -function newGame() { - E.showMenu(); - Bangle.setUI({mode : "custom", btn: () => load()}); - if (control == 4) { // Swipe +function setupControls() { + if (settings.control == 4) { // Swipe Bangle.on("touch", (e) => { t = rotateTile(ct, 3); if (moveOk(t, 0, 0)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, true); ct = t; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, false); } }); Bangle.on("swipe", (x,y) => { if (y<0) y = 0; if (moveOk(ct, x, y)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, true); px += x; py += y; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); + drawTile(ct, settings.ctn, ox+px*8, oy+py*8, false); } }); } else { // control != 4 - if (control == 2) { // Tilt + if (settings.control == 2) { // Tilt Bangle.on("accel", (e) => { if (state != 1) return; - if (control != 2) return; + if (settings.control != 2) return; print(e.x); linear((0.2-e.x) * 2.5); }); } - if (control == 3) { // Pressure + if (settings.control == 3) { // Pressure Bangle.setBarometerPower(true); Bangle.on("pressure", (e) => { if (state != 1) return; - if (control != 3) return; + if (settings.control != 3) return; let a = e.altitude; if (alt_start == -9999) alt_start = a; @@ -358,15 +380,15 @@ function newGame() { rotate(); else { while (move(0, 1)) { - score++; + settings.score++; g.flip(); } redrawStats(true); } } else { - if (control == 1) + if (settings.control == 1) linear((e.x - 20) / 156); - if (control != 0) + if (settings.control != 0) return; if (e.x < h) move(-1, 0); @@ -375,17 +397,16 @@ function newGame() { } }); } - setWatch(() => { - if (state == 1) - pauseGame(); - else if (state == 3) - resumeGame(); - }, BTN1, {repeat: true}); +} + +function newGame() { + settings.lines = 0; + settings.score = 0; + initGame(); startGame(); } function startGame() { - initGame(); calculateSpeed(); state = 1; drawGame(); @@ -393,6 +414,11 @@ function startGame() { } function drawGame() { + Bangle.setUI({mode : "custom", btn: () => { + if (state == 1) { + pauseGame(); + } + }}); drawBoundingBox(); g.setColor(1, 1, 1).setFontAlign(0, 1, 0) .setFont("6x15", 1).drawString("Score", 22, 30) @@ -400,26 +426,42 @@ function drawGame() { .drawString("Lines", 22, 130) .drawString("Next", 176-22, 30); redrawStats(); - showNext(ntn, ntr); + showNext(settings.ntn, settings.ntr); } + function selectGame() { state = 0; - print("Game selection menu"); + //print("Game selection menu"); var menu = {}; - menu["Normal"] = () => { control = 0; newGame(); }; - menu["Drag"] = () => { control = 1; newGame(); }; - menu["Tilt"] = () => { control = 2; newGame(); }; - menu["Pressure"] = () => { control = 3; newGame(); }; - menu["Swipe"] = () => { control = 4; newGame(); }; - level = 1; - menu["Level"] = { + menu[/*LANG*/"New game"] = () => { + setupControls(); + newGame(); + resumeGame(); + }; + if (settings.pf !== undefined) { + menu[/*LANG*/"Resume game"] = () => { + setupControls(); + startGame(); + resumeGame(); + }; + } + menu[/*LANG*/"Controls"] = { + value: settings.control, + min: 0, max: 4, + format: v => [/*LANG*/"Normal", /*LANG*/"Drag", /*LANG*/"Tilt", /*LANG*/"Pressure", /*LANG*/"Swipe"][v], + onchange: v => { + settings.control = v; + writeSettings(); + } + }; + menu[/*LANG*/"Level"] = { value : 1, min : 0, max : 10, wrap : true, - onchange : (l) => { level = l; } + onchange : (l) => { settings.level = l; writeSettings(); } }; E.showMenu(menu); } From e5c9286e777609c7a39857d35be4fac4ff9b644b Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Tue, 23 Jan 2024 21:32:11 +0100 Subject: [PATCH 50/70] tetris: fix level setting --- apps/tetris/tetris.app.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index d3501a9f6..91dd20ab8 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -46,6 +46,7 @@ const settings = Object.assign({ */ control: 0, level: 0, + initialLevel: 0, lines: 0, score: 0, pf: undefined, @@ -151,6 +152,7 @@ function gameOver() { E.showAlert("Game Over").then(newGame, print); settings.lines = 0; settings.score = 0; + settings.level = settings.initialLevel; } function redrawStats(onlyScore) { @@ -402,6 +404,7 @@ function setupControls() { function newGame() { settings.lines = 0; settings.score = 0; + settings.level = settings.initialLevel; initGame(); startGame(); } @@ -461,7 +464,10 @@ function selectGame() { min : 0, max : 10, wrap : true, - onchange : (l) => { settings.level = l; writeSettings(); } + onchange : (l) => { + settings.initialLevel = l; + writeSettings(); + } }; E.showMenu(menu); } From 19ee5bb8aebb1a0b7bd872f8d4ef9c5402df7217 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 25 Jan 2024 12:23:45 +0000 Subject: [PATCH 51/70] pebbled 0.06: Fix Settings page to ensure that the currently set distance is displayed (not 0.75) --- apps/pebbled/ChangeLog | 1 + apps/pebbled/metadata.json | 2 +- apps/pebbled/pebbled.settings.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/pebbled/ChangeLog b/apps/pebbled/ChangeLog index 9f5734152..fcc8b3254 100644 --- a/apps/pebbled/ChangeLog +++ b/apps/pebbled/ChangeLog @@ -4,3 +4,4 @@ Support for fast loading 0.04: Localisation request: added Miles and AM/PM 0.05: Prevent exceptions from halting the draw cycle +0.06: Fix Settings page to ensure that the currently set distance is displayed (not 0.75) diff --git a/apps/pebbled/metadata.json b/apps/pebbled/metadata.json index 483f6e0d3..50555a173 100644 --- a/apps/pebbled/metadata.json +++ b/apps/pebbled/metadata.json @@ -2,7 +2,7 @@ "id": "pebbled", "name": "Pebble Clock with distance", "shortName": "Pebble + distance", - "version": "0.05", + "version": "0.06", "description": "Fork of Pebble Clock with distance in KM. Both step count and the distance are on the main screen. Default step length = 0.75m (can be changed in settings).", "readme": "README.md", "icon": "pebbled.png", diff --git a/apps/pebbled/pebbled.settings.js b/apps/pebbled/pebbled.settings.js index f4ca1d394..d54517a70 100644 --- a/apps/pebbled/pebbled.settings.js +++ b/apps/pebbled/pebbled.settings.js @@ -36,7 +36,7 @@ }, }, 'Step length': { - value: 0.75 || s.avStep, + value: s.avStep || 0.75, min: 0.2, max: 1.5, step: 0.01, From 367ad5d285555c311b4c7cde33c3acb8d50802c2 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 26 Jan 2024 09:38:31 +0000 Subject: [PATCH 52/70] update FW update prompt --- loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader.js b/loader.js index 5f2283282..28ff540eb 100644 --- a/loader.js +++ b/loader.js @@ -16,7 +16,7 @@ if (window.location.host=="banglejs.com") { 'This is not the official Bangle.js App Loader - you can try the Official Version here.'; } -var RECOMMENDED_VERSION = "2v19"; +var RECOMMENDED_VERSION = "2v20"; // could check http://www.espruino.com/json/BANGLEJS.json for this // We're only interested in Bangles From 56b39fead96fe0cbc6bb4999b54d889e75a609ad Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sat, 27 Jan 2024 10:15:31 +0100 Subject: [PATCH 53/70] openstmap: Fix rounding errors --- apps/openstmap/ChangeLog | 1 + apps/openstmap/app.js | 6 ++++-- apps/openstmap/interface.html | 10 ++++++++-- apps/openstmap/metadata.json | 2 +- apps/openstmap/openstmap.js | 6 +++--- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/apps/openstmap/ChangeLog b/apps/openstmap/ChangeLog index 1b0edaf34..239b89abc 100644 --- a/apps/openstmap/ChangeLog +++ b/apps/openstmap/ChangeLog @@ -32,3 +32,4 @@ 0.25: Enable scaled image filtering on 2v19+ firmware 0.26: Ensure that when redrawing, we always cancel any in-progress track draw 0.27: Display message if no map is installed +0.28: Fix rounding errors diff --git a/apps/openstmap/app.js b/apps/openstmap/app.js index d66936c29..2d14fbd2a 100644 --- a/apps/openstmap/app.js +++ b/apps/openstmap/app.js @@ -65,14 +65,16 @@ function redraw() { // Draw the POIs function drawPOI() { + let waypoints; try { - var waypoints = require("waypoints").load(); + waypoints = require("waypoints").load(); } catch (ex) { // Waypoints module not available. return; } g.setFont("Vector", 18); waypoints.forEach((wp, idx) => { + if (wp.lat === undefined || wp.lon === undefined) return; var p = m.latLonToXY(wp.lat, wp.lon); var sz = 2; g.setColor(0,0,0); @@ -80,7 +82,7 @@ function drawPOI() { g.setColor(0,0,0); g.drawString(wp.name, p.x, p.y); //print(wp.name); - }) + }); } function isInside(rect, e, w, h) { diff --git a/apps/openstmap/interface.html b/apps/openstmap/interface.html index 3427fa882..a16bfa331 100644 --- a/apps/openstmap/interface.html +++ b/apps/openstmap/interface.html @@ -45,7 +45,11 @@
-

3 bit
+
+ 3 bit +
+ Preview +
+

+

+ +

+ +

+ + + + + + diff --git a/apps/avwx/metadata.json b/apps/avwx/metadata.json new file mode 100644 index 000000000..0b07f32d4 --- /dev/null +++ b/apps/avwx/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "avwx", + "name": "AVWX Module", + "shortName":"AVWX", + "version":"1.00", + "description": "Module/library for the AVWX API", + "icon": "avwx.png", + "type": "module", + "tags": "outdoors", + "supports": ["BANGLEJS2"], + "provides_modules": ["avwx"], + "readme": "README.md", + "interface": "interface.html", + "storage": [ + { "name":"avwx", "url":"avwx.js" } + ], + "data": [{ "name":"avwx.json" }] +} From 5e7c0057767c10be88b19ebdde96ae88d70dd732 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 29 Jan 2024 13:41:46 +0000 Subject: [PATCH 58/70] Fix CRC detection for older bootloaders Fixes #3172 --- apps/fwupdate/custom.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/fwupdate/custom.html b/apps/fwupdate/custom.html index 0b1387967..26cac7da2 100644 --- a/apps/fwupdate/custom.html +++ b/apps/fwupdate/custom.html @@ -96,7 +96,7 @@ function onInit(device) { if (crcs[0] == 1787004733) { // check 6 page CRC - the 7th page isn't used in 2v20 version = "2v20"; } else { // for other versions all 7 pages are used, check those - var crc = crcs[2]; + var crc = crcs[1]; if (crc==1339551013) { version = "2v10.219"; ok = false; } if (crc==1207580954) { version = "2v10.236"; ok = false; } if (crc==3435933210) version = "2v11.52"; From e9f3f657287d39e0274f12d1a584f65035b92fc3 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 29 Jan 2024 19:35:38 +0100 Subject: [PATCH 59/70] openstmap interface: Fix show maps after delete --- apps/openstmap/interface.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openstmap/interface.html b/apps/openstmap/interface.html index a16bfa331..9e14e1b2d 100644 --- a/apps/openstmap/interface.html +++ b/apps/openstmap/interface.html @@ -148,10 +148,10 @@ TODO: mapsLoadedContainer.innerHTML = ""; loadedMaps = []; - Puck.write(`\x10Bluetooth.println(require("Storage").list(/openstmap\\.\\d+\\.json/))\n`,function(files) { + Puck.eval(`require("Storage").list(/openstmap\\.\\d+\\.json/)\n`,function(files) { console.log("MAPS:",files); let promise = Promise.resolve(); - files.trim().split(",").forEach(filename => { + files.forEach(filename => { if (filename=="") return; promise = promise.then(() => new Promise(resolve => { Util.readStorageJSON(filename, mapInfo => { From a69dcdcd7ffee07210695045af2fa99a5c41323e Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 29 Jan 2024 20:01:48 +0000 Subject: [PATCH 60/70] Update EspruinoTools with pretokeniser that converts strings --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index e6a65a8cb..bdcc79ac2 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit e6a65a8cb20a730f75bbbab549c602300e69e8c4 +Subproject commit bdcc79ac2155b8eae6ce9ec4cc1065495e99ac8c From d007f82a19ab51ed42095b9407a4cb0462b3f784 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 29 Jan 2024 21:25:03 +0100 Subject: [PATCH 61/70] openstmap/interface: showLoadedMaps() adjustments Load leaflet interface after all mapps are loaded --- apps/openstmap/interface.html | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/openstmap/interface.html b/apps/openstmap/interface.html index 9e14e1b2d..b9d74d6d8 100644 --- a/apps/openstmap/interface.html +++ b/apps/openstmap/interface.html @@ -147,9 +147,11 @@ TODO: let mapsLoadedContainer = document.getElementById("mapsLoadedContainer"); mapsLoadedContainer.innerHTML = ""; loadedMaps = []; + const mapsLoaded = []; Puck.eval(`require("Storage").list(/openstmap\\.\\d+\\.json/)\n`,function(files) { console.log("MAPS:",files); + files.sort(); let promise = Promise.resolve(); files.forEach(filename => { if (filename=="") return; @@ -159,7 +161,7 @@ TODO: let mapNumber = filename.match(/\d+/)[0]; // figure out what map number we are loadedMaps[mapNumber] = mapInfo; if (mapInfo!==undefined) { - let latlon = L.latLng(mapInfo.lat, mapInfo.lon); + mapsLoaded.push({mapNumber: mapNumber, mapInfo: mapInfo}); mapsLoadedContainer.innerHTML += `
@@ -175,19 +177,27 @@ TODO:
`; - setTimeout(function() { - let map = L.map(`tile-map-${mapNumber}`); - L.tileLayer(PREVIEWTILELAYER, { - maxZoom: 18 - }).addTo(map); - let marker = new L.marker(latlon).addTo(map); - map.fitBounds(latlon.toBounds(2000/*meters*/), {animation: false}); - }, 500); } resolve(); }); })); }); + promise = promise.then(() => new Promise(resolve => { + setTimeout(() => { + mapsLoaded.forEach(mapLoaded => { + let latlon = L.latLng(mapLoaded.mapInfo.lat, mapLoaded.mapInfo.lon); + let map = L.map(`tile-map-${mapLoaded.mapNumber}`); + L.tileLayer(PREVIEWTILELAYER, { + maxZoom: 18 + }).addTo(map); + let marker = new L.marker(latlon).addTo(map); + const dist = mapLoaded.mapInfo.scale * mapLoaded.mapInfo.tilesize * mapLoaded.mapInfo.w / 2; + map.fitBounds(latlon.toBounds(dist/*meters*/), {animation: false}); + }); + }, 0); + + resolve(); + })); promise = promise.then(() => new Promise(resolve => { if (!loadedMaps.length) { mapsLoadedContainer.innerHTML += ` From fa433a47671a36eccfaf816ddb2fd39f491ae1db Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Tue, 30 Jan 2024 07:30:11 +0100 Subject: [PATCH 62/70] openstmap interface move diameter to radius conversion into toBounds() --- apps/openstmap/interface.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openstmap/interface.html b/apps/openstmap/interface.html index b9d74d6d8..df5c0acc9 100644 --- a/apps/openstmap/interface.html +++ b/apps/openstmap/interface.html @@ -191,8 +191,8 @@ TODO: maxZoom: 18 }).addTo(map); let marker = new L.marker(latlon).addTo(map); - const dist = mapLoaded.mapInfo.scale * mapLoaded.mapInfo.tilesize * mapLoaded.mapInfo.w / 2; - map.fitBounds(latlon.toBounds(dist/*meters*/), {animation: false}); + const dist = mapLoaded.mapInfo.scale * mapLoaded.mapInfo.tilesize * mapLoaded.mapInfo.w; + map.fitBounds(latlon.toBounds(dist/2/*meters*/), {animation: false}); }); }, 0); From a69be50414703e3e882bc8f5edd5b5a126c7f91b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 30 Jan 2024 15:14:45 +0000 Subject: [PATCH 63/70] oops - allow fw update for pre-2v20 --- apps/fwupdate/custom.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/fwupdate/custom.html b/apps/fwupdate/custom.html index 26cac7da2..b917da87b 100644 --- a/apps/fwupdate/custom.html +++ b/apps/fwupdate/custom.html @@ -115,7 +115,7 @@ function onInit(device) { } document.getElementById("boot-version").innerHTML = version; var versionNumber = parseFloat(version.replace(".","").replace("v",".")); - if (versionNumber>=2.15) + if (versionNumber>=2.20) document.getElementById("fw-old-bootloader-msg").style.display = "none"; }); } From 168a018b00b6581a2bcd10583393ecace877a838 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 30 Jan 2024 15:15:32 +0000 Subject: [PATCH 64/70] update firmwaremaker to use new pretokenisation --- bin/firmwaremaker_c.js | 27 +++++++++++++++++++++++---- core | 2 +- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/bin/firmwaremaker_c.js b/bin/firmwaremaker_c.js index bd1e1f564..b932f0b84 100755 --- a/bin/firmwaremaker_c.js +++ b/bin/firmwaremaker_c.js @@ -13,6 +13,7 @@ var path = require('path'); var fs = require("fs"); var ROOTDIR = path.join(__dirname, '..'); var OUTFILE, APPS; +var JSUTILS = path.join(ROOTDIR, '../Espruino/src/jsutils.h'); if (DEVICEID=="BANGLEJS") { var OUTFILE = path.join(ROOTDIR, '../Espruino/libs/banglejs/banglejs1_storage_default.c'); @@ -34,9 +35,18 @@ if (DEVICEID=="BANGLEJS") { } console.log("Device = ",DEVICEID); +// Search for version String +var VERSION = "2v10"; +var m = require("fs").readFileSync(JSUTILS).toString().match(/#define\s*JS_VERSION\s*([^\n]*)/); +if (m) { + VERSION = JSON.parse(m[1]); +} +console.log("Using version "+VERSION); + var apploader = require("../core/lib/apploader.js"); apploader.init({ - DEVICEID : DEVICEID + DEVICEID : DEVICEID, + VERSION : VERSION, }); @@ -79,11 +89,13 @@ var appfiles = []; // If file should be evaluated, try and do it... function evaluateFile(file) { + var content = file.content.trim(); var hsStart = 'require("heatshrink").decompress(atob("'; var hsEnd = '"))'; - if (file.content.startsWith(hsStart) && file.content.endsWith(hsEnd)) { + if (content.startsWith(hsStart) && content.endsWith(hsEnd)) { + console.log("heatshrink"); var heatshrink = require(ROOTDIR+"/webtools/heatshrink.js"); - var b64 = file.content.slice(hsStart.length, -hsEnd.length); + var b64 = content.slice(hsStart.length, -hsEnd.length); var decompressed = heatshrink.decompress(atob(b64)); file.content = ""; for (var i=0;i { files.forEach(f => { var existing = appfiles.find(a=> a.name==f.name); if (existing) { - if (existing.content !== f.content) + if (existing.content !== f.content) { + console.log("========================================="); + console.log(`Duplicate file ${f.name} is different`); + console.log("EXISTING", existing.content); + console.log("NEW", f.content); throw new Error(`Duplicate file ${f.name} is different`) + } } else { appfiles.push(f); } diff --git a/core b/core index bdcc79ac2..364b2c1b0 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit bdcc79ac2155b8eae6ce9ec4cc1065495e99ac8c +Subproject commit 364b2c1b00de17ffbbee87fb1d91e79b513b9127 From d8ada3f734c11f39669b51402840aa45feda2ce6 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 30 Jan 2024 16:36:44 +0000 Subject: [PATCH 65/70] Refactor to remove the (apparently spurious) `Error: Unreachable point. logically broken.` warning during minification --- modules/widget_utils.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/widget_utils.js b/modules/widget_utils.js index 1c7d0ed28..9d20e6803 100644 --- a/modules/widget_utils.js +++ b/modules/widget_utils.js @@ -47,7 +47,7 @@ exports.cleanup = function() { Bangle.drawWidgets = exports.origDraw; delete exports.origDraw; } -} +}; /** Put widgets offscreen, and allow them to be swiped back onscreen with a downwards swipe. Use .show to undo. @@ -87,8 +87,7 @@ exports.swipeOn = function(autohide) { else Bangle.setLCDOverlay(); } - for (var w of global.WIDGETS) { - if (w._draw) continue; // already hidden + for (var w of global.WIDGETS) if (!w._draw) { // already hidden w._draw = w.draw; w.draw = function() { g=og; @@ -139,10 +138,9 @@ exports.swipeOn = function(autohide) { exports.hideTimeout = setTimeout(function() { anim(-4); }, exports.autohide); - } + }; if (ud>0 && offset<0) anim(4, cb); if (ud<0 && offset>-24) anim(-4); - }; Bangle.on("swipe", exports.swipeHandler); Bangle.drawWidgets(); From b705c4bc748778cdbb4007afbf6a6657df05b436 Mon Sep 17 00:00:00 2001 From: EnerGeh Date: Wed, 31 Jan 2024 00:32:01 +0100 Subject: [PATCH 66/70] Update lib.js Added Molly (Signal Form) and Threma libre (official f-droid flavour of threema). --- apps/messageicons/lib.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/messageicons/lib.js b/apps/messageicons/lib.js index f7efa2d16..306a4ae46 100644 --- a/apps/messageicons/lib.js +++ b/apps/messageicons/lib.js @@ -2,7 +2,7 @@ exports.getImage = function(msg) { if (msg.img) return atob(msg.img); let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); if (msg.id=="music") s="music"; - let match = ",default|0,airbnb|1,alarm|2,alarmclockreceiver|2,amazon shopping|3,bibel|4,bitwarden|5,1password|5,lastpass|5,dashlane|5,bring|6,calendar|7,etar|7,chat|8,chrome|9,clock|2,corona-warn|10,bmo|11,desjardins|11,rbc mobile|11,nbc|11,rabobank|11,scotiabank|11,td (canada)|11,discord|12,drive|13,element|14,facebook|15,messenger|16,firefox|17,firefox beta|17,firefox nightly|17,f-droid|5,neo store|5,aurora droid|5,github|18,gitlab|19,gmx|20,google|21,google home|22,google play store|23,home assistant|24,instagram|25,jira|26,kalender|27,keep notes|28,lieferando|29,linkedin|30,maps|31,organic maps|31,osmand|31,mastodon|32,fedilab|32,tooot|32,tusky|32,mattermost|33,n26|34,netflix|35,news|36,cbc news|36,rc info|36,reuters|36,ap news|36,la presse|36,nbc news|36,nextbike|37,nina|38,outlook mail|39,paypal|40,phone|41,plex|42,pocket|43,post & dhl|44,proton mail|45,reddit|46,sync pro|46,sync dev|46,boost|46,infinity|46,slide|46,signal|47,skype|48,slack|49,snapchat|50,starbucks|51,steam|52,teams|53,telegram|54,telegram foss|54,threema|55,tiktok|56,to do|57,opentasks|57,tasks|57,transit|58,twitch|59,twitter|60,uber|61,lyft|61,vlc|62,warnapp|63,whatsapp|64,wordfeud|65,youtube|66,newpipe|66,zoom|67,meet|67,music|68,sms message|0,mail|0,gmail|0,".match(new RegExp(`,${s}\\|(\\d+)`)) + let match = ",default|0,airbnb|1,alarm|2,alarmclockreceiver|2,amazon shopping|3,bibel|4,bitwarden|5,1password|5,lastpass|5,dashlane|5,bring|6,calendar|7,etar|7,chat|8,chrome|9,clock|2,corona-warn|10,bmo|11,desjardins|11,rbc mobile|11,nbc|11,rabobank|11,scotiabank|11,td (canada)|11,discord|12,drive|13,element|14,facebook|15,messenger|16,firefox|17,firefox beta|17,firefox nightly|17,f-droid|5,neo store|5,aurora droid|5,github|18,gitlab|19,gmx|20,google|21,google home|22,google play store|23,home assistant|24,instagram|25,jira|26,kalender|27,keep notes|28,lieferando|29,linkedin|30,maps|31,organic maps|31,osmand|31,mastodon|32,fedilab|32,tooot|32,tusky|32,mattermost|33,n26|34,netflix|35,news|36,cbc news|36,rc info|36,reuters|36,ap news|36,la presse|36,nbc news|36,nextbike|37,nina|38,outlook mail|39,paypal|40,phone|41,plex|42,pocket|43,post & dhl|44,proton mail|45,reddit|46,sync pro|46,sync dev|46,boost|46,infinity|46,slide|46,signal|47,molly|47,skype|48,slack|49,snapchat|50,starbucks|51,steam|52,teams|53,telegram|54,telegram foss|54,threema|55,threema libre|55,tiktok|56,to do|57,opentasks|57,tasks|57,transit|58,twitch|59,twitter|60,uber|61,lyft|61,vlc|62,warnapp|63,whatsapp|64,wordfeud|65,youtube|66,newpipe|66,zoom|67,meet|67,music|68,sms message|0,mail|0,gmail|0,".match(new RegExp(`,${s}\\|(\\d+)`)) return require("Storage").read("messageicons.img", (match===null)?0:match[1]*76, 76); }; @@ -67,4 +67,4 @@ exports.getColor = function(msg,options) { "youtube": "#f00", // https://www.youtube.com/howyoutubeworks/resources/brand-resources/#logos-icons-and-colors }[s]||options.default; }; - \ No newline at end of file + From 7af324f2b4a9857d8a7382e8805c888a3cb595fd Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Wed, 31 Jan 2024 19:07:42 +0100 Subject: [PATCH 67/70] Revert "openstmap menu: move exit to top" This reverts commit c4631cfe5f8bdc0fad3ce3d163a790d81a6078c8. --- apps/openstmap/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openstmap/app.js b/apps/openstmap/app.js index 7c015cfc8..2d14fbd2a 100644 --- a/apps/openstmap/app.js +++ b/apps/openstmap/app.js @@ -172,7 +172,6 @@ function showMenu() { var menu = { "":{title:/*LANG*/"Map"}, "< Back": ()=> showMap(), - /*LANG*/"Exit": () => load(), }; // If we have a GPS fix, add a menu item to center it if (fix.fix) menu[/*LANG*/"Center GPS"]=() =>{ @@ -180,7 +179,6 @@ function showMenu() { m.lon = fix.lon; showMap(); }; - menu = Object.assign(menu, { /*LANG*/"Zoom In": () =>{ m.scale /= 2; @@ -234,6 +232,7 @@ function showMenu() { } }; } + menu[/*LANG*/"Exit"] = () => load(); E.showMenu(menu); } From d9364b32a6774f5b68860240f6ec092c0f6c6553 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Wed, 31 Jan 2024 19:08:34 +0100 Subject: [PATCH 68/70] openstmap: Update ChangeLog, metadata.json --- apps/openstmap/ChangeLog | 1 + apps/openstmap/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openstmap/ChangeLog b/apps/openstmap/ChangeLog index 239b89abc..98d01d5e9 100644 --- a/apps/openstmap/ChangeLog +++ b/apps/openstmap/ChangeLog @@ -33,3 +33,4 @@ 0.26: Ensure that when redrawing, we always cancel any in-progress track draw 0.27: Display message if no map is installed 0.28: Fix rounding errors +0.29: move exit to bottom of menu diff --git a/apps/openstmap/metadata.json b/apps/openstmap/metadata.json index 2b010c576..904c1bd94 100644 --- a/apps/openstmap/metadata.json +++ b/apps/openstmap/metadata.json @@ -2,7 +2,7 @@ "id": "openstmap", "name": "OpenStreetMap", "shortName": "OpenStMap", - "version": "0.28", + "version": "0.29", "description": "Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are. Once installed this also adds map functionality to `GPS Recorder` and `Recorder` apps", "readme": "README.md", "icon": "app.png", From 2c96f33f00a7cedf23b940c7e604355988c1c32f Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:39:56 -0600 Subject: [PATCH 69/70] android: Fix alarms created in Gadgetbridge not repeating --- apps/android/ChangeLog | 1 + apps/android/boot.js | 8 +++++++- apps/android/metadata.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index d531e43a9..108242825 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -32,3 +32,4 @@ Allow alarm enable/disable 0.31: Implement API for activity fetching 0.32: Added support for loyalty cards from gadgetbridge +0.33: Fix alarms created in Gadgetbridge not repeating diff --git a/apps/android/boot.js b/apps/android/boot.js index 846fc40a8..63f9b2883 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -81,7 +81,12 @@ for (var j = 0; j < event.d.length; j++) { // prevents all alarms from going off at once?? var dow = event.d[j].rep; - if (!dow) dow = 127; //if no DOW selected, set alarm to all DOW + var rp = false; + if (!dow) { + dow = 127; //if no DOW selected, set alarm to all DOW + } else { + rp = true; + } var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0; var a = require("sched").newDefaultAlarm(); a.id = "gb"+j; @@ -89,6 +94,7 @@ a.on = event.d[j].on !== undefined ? event.d[j].on : true; a.t = event.d[j].h * 3600000 + event.d[j].m * 60000; a.dow = ((dow&63)<<1) | (dow>>6); // Gadgetbridge sends DOW in a different format + a.rp = rp; a.last = last; alarms.push(a); } diff --git a/apps/android/metadata.json b/apps/android/metadata.json index 68bd946c5..5babc520b 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,7 +2,7 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.32", + "version": "0.33", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications,gadgetbridge", From 69071891f1deef3a1485f4a7822b6570a688c6bf Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 2 Feb 2024 09:34:36 +0000 Subject: [PATCH 70/70] Tweaks to speed up track rendering --- apps/openstmap/ChangeLog | 3 +- apps/openstmap/openstmap.js | 22 +- apps/recorder/ChangeLog | 1 + apps/recorder/app.js | 414 ++++++++++++++++++------------------ apps/recorder/metadata.json | 2 +- 5 files changed, 221 insertions(+), 221 deletions(-) diff --git a/apps/openstmap/ChangeLog b/apps/openstmap/ChangeLog index 98d01d5e9..f0a1e5c5a 100644 --- a/apps/openstmap/ChangeLog +++ b/apps/openstmap/ChangeLog @@ -33,4 +33,5 @@ 0.26: Ensure that when redrawing, we always cancel any in-progress track draw 0.27: Display message if no map is installed 0.28: Fix rounding errors -0.29: move exit to bottom of menu +0.29: Keep exit at bottom of menu + Speed up latLonToXY for track rendering \ No newline at end of file diff --git a/apps/openstmap/openstmap.js b/apps/openstmap/openstmap.js index 7e84f66e5..eb1aeaf3c 100644 --- a/apps/openstmap/openstmap.js +++ b/apps/openstmap/openstmap.js @@ -38,17 +38,17 @@ if (m.map) { m.lat = m.map.lat; // position of middle of screen m.lon = m.map.lon; // position of middle of screen } +var CX = g.getWidth()/2; +var CY = g.getHeight()/2; // return number of tiles drawn exports.draw = function() { - var cx = g.getWidth()/2; - var cy = g.getHeight()/2; var p = Bangle.project({lat:m.lat,lon:m.lon}); let count = 0; m.maps.forEach((map,idx) => { var d = map.scale/m.scale; - var ix = (p.x-map.center.x)/m.scale + (map.imgx*d/2) - cx; - var iy = (map.center.y-p.y)/m.scale + (map.imgy*d/2) - cy; + var ix = (p.x-map.center.x)/m.scale + (map.imgx*d/2) - CX; + var iy = (map.center.y-p.y)/m.scale + (map.imgy*d/2) - CY; var o = {}; var s = map.tilesize; if (d!=1) { // if the two are different, add scaling @@ -85,14 +85,12 @@ exports.draw = function() { }; /// Convert lat/lon to pixels on the screen -exports.latLonToXY = function(lat, lon) { - var p = Bangle.project({lat:m.lat,lon:m.lon}); - var q = Bangle.project({lat:lat, lon:lon}); - var cx = g.getWidth()/2; - var cy = g.getHeight()/2; +exports.latLonToXY = function(lat, lon) { "ram" + var p = Bangle.project({lat:m.lat,lon:m.lon}), + q = Bangle.project({lat:lat, lon:lon}); return { - x : Math.round((q.x-p.x)/m.scale + cx), - y : Math.round(cy - (q.y-p.y)/m.scale) + x : Math.round((q.x-p.x)/m.scale + CX), + y : Math.round(CY - (q.y-p.y)/m.scale) }; }; @@ -102,4 +100,4 @@ exports.scroll = function(x,y) { var b = Bangle.project({lat:m.lat+1,lon:m.lon+1}); this.lon += x * m.scale / (a.x-b.x); this.lat -= y * m.scale / (a.y-b.y); -}; +}; \ No newline at end of file diff --git a/apps/recorder/ChangeLog b/apps/recorder/ChangeLog index dadd3fbcb..0e20a13fc 100644 --- a/apps/recorder/ChangeLog +++ b/apps/recorder/ChangeLog @@ -45,3 +45,4 @@ 0.36: When recording with 1 second periods, log time with one decimal. 0.37: 1 second periods + gps log => log when gps event is received, not with setInterval. +0.38: Tweaks to speed up track rendering \ No newline at end of file diff --git a/apps/recorder/app.js b/apps/recorder/app.js index a2218420a..c02a800d0 100644 --- a/apps/recorder/app.js +++ b/apps/recorder/app.js @@ -213,230 +213,230 @@ function viewTrack(filename, info) { }); }; menu['< Back'] = () => { viewTracks(); }; + return E.showMenu(menu); +} - function plotTrack(info) { "ram" - function distance(lat1,long1,lat2,long2) { "ram" - var x = (long1-long2) * Math.cos((lat1+lat2)*Math.PI/360); - var y = lat2 - lat1; - return Math.sqrt(x*x + y*y) * 6371000 * Math.PI / 180; - } +function plotTrack(info) { "ram" + function distance(lat1,long1,lat2,long2) { "ram" + var x = (long1-long2) * Math.cos((lat1+lat2)*Math.PI/360); + var y = lat2 - lat1; + return Math.sqrt(x*x + y*y) * 6371000 * Math.PI / 180; + } - // Function to convert lat/lon to XY - var getMapXY; - if (info.qOSTM) { - // scale map to view full track - const max = Bangle.project({lat: info.maxLat, lon: info.maxLong}); - const min = Bangle.project({lat: info.minLat, lon: info.minLong}); - const scaleX = (max.x-min.x)/Bangle.appRect.w; - const scaleY = (max.y-min.y)/Bangle.appRect.h; - osm.scale = Math.ceil((scaleX > scaleY ? scaleX : scaleY)*1.1); // add 10% margin - getMapXY = osm.latLonToXY.bind(osm); - } else { - getMapXY = function(lat, lon) { "ram" - return {x:cx + Math.round((long - info.lon)*info.lfactor*info.scale), - y:cy + Math.round((info.lat - lat)*info.scale)}; - }; - } + // Function to convert lat/lon to XY + var XY; + if (info.qOSTM) { + // scale map to view full track + const max = Bangle.project({lat: info.maxLat, lon: info.maxLong}); + const min = Bangle.project({lat: info.minLat, lon: info.minLong}); + const scaleX = (max.x-min.x)/Bangle.appRect.w; + const scaleY = (max.y-min.y)/Bangle.appRect.h; + osm.scale = Math.ceil((scaleX > scaleY ? scaleX : scaleY)*1.1); // add 10% margin + XY = osm.latLonToXY.bind(osm); + } else { + XY = function(lat, lon) { "ram" + return {x:cx + Math.round((long - info.lon)*info.lfactor*info.scale), + y:cy + Math.round((info.lat - lat)*info.scale)}; + }; + } - E.showMenu(); // remove menu - E.showMessage(/*LANG*/"Drawing...",/*LANG*/"Track "+info.fn); - g.flip(); // on buffered screens, draw a not saying we're busy - g.clear(1); - var s = require("Storage"); - var W = g.getWidth(); - var H = g.getHeight(); - var cx = W/2; - var cy = 24 + (H-24)/2; - if (!info.qOSTM) { - g.setColor("#f00").fillRect(9,80,11,120).fillPoly([9,60,19,80,0,80]); - g.setColor(g.theme.fg).setFont("6x8").setFontAlign(0,0).drawString("N",10,50); - } else { - osm.lat = info.lat; - osm.lon = info.lon; - osm.draw(); - g.setColor("#000"); + E.showMenu(); // remove menu + E.showMessage(/*LANG*/"Drawing...",/*LANG*/"Track "+info.fn); + g.flip(); // on buffered screens, draw a not saying we're busy + g.clear(1); + var s = require("Storage"); + var G = g; + var W = g.getWidth(); + var H = g.getHeight(); + var cx = W/2; + var cy = 24 + (H-24)/2; + if (!info.qOSTM) { + g.setColor("#f00").fillRect(9,80,11,120).fillPoly([9,60,19,80,0,80]); + g.setColor(g.theme.fg).setFont("6x8").setFontAlign(0,0).drawString("N",10,50); + } else { + osm.lat = info.lat; + osm.lon = info.lon; + osm.draw(); + g.setColor("#000"); + } + var latIdx = info.fields.indexOf("Latitude"); + var lonIdx = info.fields.indexOf("Longitude"); + g.drawString(asTime(info.duration),10,220); + var f = require("Storage").open(info.filename,"r"); + if (f===undefined) return; + var l = f.readLine(f); + l = f.readLine(f); // skip headers + var ox=0; + var oy=0; + var olat,olong,dist=0; + var i=0, c = l.split(","); + // skip until we find our first data + while(l!==undefined && c[latIdx]=="") { + c = l.split(","); + l = f.readLine(f); + } + // now start plotting + var lat = +c[latIdx]; + var long = +c[lonIdx]; + var mp = XY(lat, long); + g.moveTo(mp.x,mp.y); + g.setColor("#0f0"); + g.fillCircle(mp.x,mp.y,5); + if (info.qOSTM) g.setColor("#f09"); + else g.setColor(g.theme.fg); + l = f.readLine(f); + g.flip(); // force update + while(l!==undefined) { + c = l.split(",");l = f.readLine(f); + if (c[latIdx]=="")continue; + lat = +c[latIdx]; + long = +c[lonIdx]; + mp = XY(lat, long); + G.lineTo(mp.x,mp.y); + if (info.qOSTM) G.fillCircle(mp.x,mp.y,2); // make the track more visible + var d = distance(olat,olong,lat,long); + if (!isNaN(d)) dist+=d; + olat = lat; + olong = long; + ox = mp.x; + oy = mp.y; + if (++i > 100) { G.flip();i=0; } + } + g.setColor("#f00"); + g.fillCircle(ox,oy,5); + if (info.qOSTM) g.setColor("#000"); + else g.setColor(g.theme.fg); + g.drawString(require("locale").distance(dist,2),g.getWidth() / 2, g.getHeight() - 20); + g.setFont("6x8",2); + g.setFontAlign(0,0,3); + var isBTN3 = "BTN3" in global; + g.drawString(/*LANG*/"Back",g.getWidth() - 10, isBTN3 ? (g.getHeight() - 40) : (g.getHeight()/2)); + setWatch(function() { + viewTrack(info.fn, info); + }, isBTN3?BTN3:BTN1); + Bangle.drawWidgets(); + g.flip(); +} + +function plotGraph(info, style) { "ram" + E.showMenu(); // remove menu + E.showMessage(/*LANG*/"Calculating...",/*LANG*/"Track "+info.fn); + var filename = info.filename; + var infn = new Float32Array(80); + var infc = new Uint16Array(80); + var title; + var lt = 0; // last time + var tn = 0; // count for each time period + var strt, dur = info.duration; + var f = require("Storage").open(filename,"r"); + if (f===undefined) return; + var l = f.readLine(f); + l = f.readLine(f); // skip headers + var nl = 0, c, i; + var factor = 1; // multiplier used for values when graphing + var timeIdx = info.fields.indexOf("Time"); + if (l!==undefined) { + c = l.split(","); + strt = c[timeIdx]; + } + if (style=="Heartrate") { + title = /*LANG*/"Heartrate (bpm)"; + var hrmIdx = info.fields.indexOf("Heartrate"); + while(l!==undefined) { + ++nl;c=l.split(",");l = f.readLine(f); + if (c[hrmIdx]=="") continue; + i = Math.round(80*(c[timeIdx] - strt)/dur); + infn[i]+=+c[hrmIdx]; + infc[i]++; } + } else if (style=="Altitude") { + title = /*LANG*/"Altitude (m)"; + var altIdx = info.fields.indexOf("Barometer Altitude"); + if (altIdx<0) altIdx = info.fields.indexOf("Altitude"); + while(l!==undefined) { + ++nl;c=l.split(",");l = f.readLine(f); + if (c[altIdx]=="") continue; + i = Math.round(80*(c[timeIdx] - strt)/dur); + infn[i]+=+c[altIdx]; + infc[i]++; + } + } else if (style=="Speed") { + // use locate to work out units + var localeStr = require("locale").speed(1,5); // get what 1kph equates to + let units = localeStr.replace(/[0-9.]*/,""); + factor = parseFloat(localeStr)*3.6; // m/sec to whatever out units are + // title + title = /*LANG*/"Speed"+` (${units})`; var latIdx = info.fields.indexOf("Latitude"); var lonIdx = info.fields.indexOf("Longitude"); - g.drawString(asTime(info.duration),10,220); - var f = require("Storage").open(info.filename,"r"); - if (f===undefined) return; - var l = f.readLine(f); - l = f.readLine(f); // skip headers - var ox=0; - var oy=0; - var olat,olong,dist=0; - var i=0, c = l.split(","); // skip until we find our first data while(l!==undefined && c[latIdx]=="") { c = l.split(","); l = f.readLine(f); } - // now start plotting - var lat = +c[latIdx]; - var long = +c[lonIdx]; - var mp = getMapXY(lat, long); - g.moveTo(mp.x,mp.y); - g.setColor("#0f0"); - g.fillCircle(mp.x,mp.y,5); - if (info.qOSTM) g.setColor("#f09"); - else g.setColor(g.theme.fg); - l = f.readLine(f); - g.flip(); // force update + // now iterate + var p,lp = Bangle.project({lat:c[1],lon:c[2]}); + var t,dx,dy,d,lt = c[timeIdx]; while(l!==undefined) { - c = l.split(",");l = f.readLine(f); - if (c[latIdx]=="")continue; - lat = +c[latIdx]; - long = +c[lonIdx]; - mp = getMapXY(lat, long); - g.lineTo(mp.x,mp.y); - if (info.qOSTM) g.fillCircle(mp.x,mp.y,2); // make the track more visible - var d = distance(olat,olong,lat,long); - if (!isNaN(d)) dist+=d; - olat = lat; - olong = long; - ox = mp.x; - oy = mp.y; - if (++i > 100) { g.flip();i=0; } - } - g.setColor("#f00"); - g.fillCircle(ox,oy,5); - if (info.qOSTM) g.setColor("#000"); - else g.setColor(g.theme.fg); - g.drawString(require("locale").distance(dist,2),g.getWidth() / 2, g.getHeight() - 20); - g.setFont("6x8",2); - g.setFontAlign(0,0,3); - var isBTN3 = "BTN3" in global; - g.drawString(/*LANG*/"Back",g.getWidth() - 10, isBTN3 ? (g.getHeight() - 40) : (g.getHeight()/2)); - setWatch(function() { - viewTrack(info.fn, info); - }, isBTN3?BTN3:BTN1); - Bangle.drawWidgets(); - g.flip(); - } - - function plotGraph(info, style) { "ram" - E.showMenu(); // remove menu - E.showMessage(/*LANG*/"Calculating...",/*LANG*/"Track "+info.fn); - var filename = info.filename; - var infn = new Float32Array(80); - var infc = new Uint16Array(80); - var title; - var lt = 0; // last time - var tn = 0; // count for each time period - var strt, dur = info.duration; - var f = require("Storage").open(filename,"r"); - if (f===undefined) return; - var l = f.readLine(f); - l = f.readLine(f); // skip headers - var nl = 0, c, i; - var factor = 1; // multiplier used for values when graphing - var timeIdx = info.fields.indexOf("Time"); - if (l!==undefined) { - c = l.split(","); - strt = c[timeIdx]; - } - if (style=="Heartrate") { - title = /*LANG*/"Heartrate (bpm)"; - var hrmIdx = info.fields.indexOf("Heartrate"); - while(l!==undefined) { - ++nl;c=l.split(",");l = f.readLine(f); - if (c[hrmIdx]=="") continue; - i = Math.round(80*(c[timeIdx] - strt)/dur); - infn[i]+=+c[hrmIdx]; + ++nl;c=l.split(","); + l = f.readLine(f); + if (c[latIdx] == "") { + continue; + } + t = c[timeIdx]; + i = Math.round(80*(t - strt)/dur); + p = Bangle.project({lat:c[latIdx],lon:c[lonIdx]}); + dx = p.x-lp.x; + dy = p.y-lp.y; + d = Math.sqrt(dx*dx+dy*dy); + if (t!=lt) { + infn[i]+=d / (t-lt); // speed infc[i]++; } - } else if (style=="Altitude") { - title = /*LANG*/"Altitude (m)"; - var altIdx = info.fields.indexOf("Barometer Altitude"); - if (altIdx<0) altIdx = info.fields.indexOf("Altitude"); - while(l!==undefined) { - ++nl;c=l.split(",");l = f.readLine(f); - if (c[altIdx]=="") continue; - i = Math.round(80*(c[timeIdx] - strt)/dur); - infn[i]+=+c[altIdx]; - infc[i]++; - } - } else if (style=="Speed") { - // use locate to work out units - var localeStr = require("locale").speed(1,5); // get what 1kph equates to - let units = localeStr.replace(/[0-9.]*/,""); - factor = parseFloat(localeStr)*3.6; // m/sec to whatever out units are - // title - title = /*LANG*/"Speed"+` (${units})`; - var latIdx = info.fields.indexOf("Latitude"); - var lonIdx = info.fields.indexOf("Longitude"); - // skip until we find our first data - while(l!==undefined && c[latIdx]=="") { - c = l.split(","); - l = f.readLine(f); - } - // now iterate - var p,lp = Bangle.project({lat:c[1],lon:c[2]}); - var t,dx,dy,d,lt = c[timeIdx]; - while(l!==undefined) { - ++nl;c=l.split(","); - l = f.readLine(f); - if (c[latIdx] == "") { - continue; - } - t = c[timeIdx]; - i = Math.round(80*(t - strt)/dur); - p = Bangle.project({lat:c[latIdx],lon:c[lonIdx]}); - dx = p.x-lp.x; - dy = p.y-lp.y; - d = Math.sqrt(dx*dx+dy*dy); - if (t!=lt) { - infn[i]+=d / (t-lt); // speed - infc[i]++; - } - lp = p; - lt = t; - } - } else throw new Error("Unknown type "+style); - var min=100000,max=-100000; - for (var i=0;i0) infn[i]=factor*infn[i]/infc[i]; - else { // no data - search back and see if we can find something - for (var j=i-1;j>=0;j--) - if (infc[j]) { infn[i]=infn[j]; break; } - } - var n = infn[i]; - if (n>max) max=n; - if (n 8) { - grid*=2; + } else throw new Error("Unknown type "+style); + var min=100000,max=-100000; + for (var i=0;i0) infn[i]=factor*infn[i]/infc[i]; + else { // no data - search back and see if we can find something + for (var j=i-1;j>=0;j--) + if (infc[j]) { infn[i]=infn[j]; break; } } - // draw - g.clear(1).setFont("6x8",1); - var r = require("graph").drawLine(g, infn, { - x:4,y:24, - width: g.getWidth()-24, - height: g.getHeight()-(24+8), - axes : true, - gridy : grid, - gridx : infn.length / 3, - title: title, - miny: min, - maxy: max, - xlabel : x=>Math.round(x*dur/(60*infn.length))+/*LANG*/" min" // minutes - }); - g.setFont("6x8",2); - g.setFontAlign(0,0,3); - var isBTN3 = "BTN3" in global; - g.drawString(/*LANG*/"Back",g.getWidth() - 10, isBTN3 ? (g.getHeight() - 40) : (g.getHeight()/2)); - setWatch(function() { - viewTrack(info.filename, info); - }, isBTN3?BTN3:BTN1); - g.flip(); + var n = infn[i]; + if (n>max) max=n; + if (n 8) { + grid*=2; + } + // draw + g.clear(1).setFont("6x8",1); + var r = require("graph").drawLine(g, infn, { + x:4,y:24, + width: g.getWidth()-24, + height: g.getHeight()-(24+8), + axes : true, + gridy : grid, + gridx : infn.length / 3, + title: title, + miny: min, + maxy: max, + xlabel : x=>Math.round(x*dur/(60*infn.length))+/*LANG*/" min" // minutes + }); + g.setFont("6x8",2); + g.setFontAlign(0,0,3); + var isBTN3 = "BTN3" in global; + g.drawString(/*LANG*/"Back",g.getWidth() - 10, isBTN3 ? (g.getHeight() - 40) : (g.getHeight()/2)); + setWatch(function() { + viewTrack(info.filename, info); + }, isBTN3?BTN3:BTN1); + g.flip(); } -showMainMenu(); +showMainMenu(); \ No newline at end of file diff --git a/apps/recorder/metadata.json b/apps/recorder/metadata.json index b0f42e1b4..a231a98e9 100644 --- a/apps/recorder/metadata.json +++ b/apps/recorder/metadata.json @@ -2,7 +2,7 @@ "id": "recorder", "name": "Recorder", "shortName": "Recorder", - "version": "0.37", + "version": "0.38", "description": "Record GPS position, heart rate and more in the background, then download to your PC.", "icon": "app.png", "tags": "tool,outdoors,gps,widget,clkinfo",