From 6614b93e959c8b928447897cfab4b49f1f54e820 Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Sat, 9 Mar 2024 15:58:00 +1100 Subject: [PATCH 01/23] feat: hassio --- apps/hassio/README.md | 41 ++++++++++++ apps/hassio/hassio.app.js | 132 +++++++++++++++++++++++++++++++++++++ apps/hassio/hassio.boot.js | 128 +++++++++++++++++++++++++++++++++++ apps/hassio/hassio.img | Bin 0 -> 292 bytes apps/hassio/hassio.json | 13 ++++ apps/hassio/hassio.png | Bin 0 -> 980 bytes apps/hassio/interface.html | 106 +++++++++++++++++++++++++++++ apps/hassio/metadata.json | 19 ++++++ 8 files changed, 439 insertions(+) create mode 100644 apps/hassio/README.md create mode 100644 apps/hassio/hassio.app.js create mode 100644 apps/hassio/hassio.boot.js create mode 100644 apps/hassio/hassio.img create mode 100644 apps/hassio/hassio.json create mode 100644 apps/hassio/hassio.png create mode 100644 apps/hassio/interface.html create mode 100644 apps/hassio/metadata.json diff --git a/apps/hassio/README.md b/apps/hassio/README.md new file mode 100644 index 000000000..70703c3fb --- /dev/null +++ b/apps/hassio/README.md @@ -0,0 +1,41 @@ +# Home Assistant API Interface + +This app provides two features: + +- Sending health, compass, accelerometer, and battery information to [Home Assistant](https://www.home-assistant.io/) +- Displaying [Home Assistant](https://www.home-assistant.io/) templates + +This is done through rest api calls to your [Home Assistant](https://www.home-assistant.io/) server. This means the app requires using the [Android Integration](/?id=android) and for your server to be accessible from your phone. + +A restart may be required after loading the app to start the background sensor process. + +## Configuration + +Configuration is done through modifying the settings json. + +```json +{ + "templates": [ + { + "name":"Test Template", + "temp":"Test" + } + ], + "interval": 180000, + "api_key":"api_key", + "host":"https://homeassistant:8123", + "id":"banglejs", + "friendly_name":"Banglejs Sensors" +} +``` + +- `api_key`: A [Home Assistant](https://www.home-assistant.io/) [api key](https://developers.home-assistant.io/docs/api/rest/). +- `host`: The url of your [Home Assistant](https://www.home-assistant.io/) server. The url must be https or it will not work. This is a limitation of the permissions given to the Banglejs GadgetBridge app. You can compile a custom version if you wish to modify this. +- `interval`: The sensor update interval. +- `id`: An id to be used for identifying your banglejs in [Home Assistant](https://www.home-assistant.io/). +- `friendly_name`: The name [Home Assistant](https://www.home-assistant.io/) will use to refer to your banglejs. +- `templates`: A list of templates to display in the gui. They are given in this format `{"name":"Template Name", "temp":"A template"}`. More information about creating templates can be found [here](https://www.home-assistant.io/docs/configuration/templating/). + +## The GUI + +The GUI will display templates one at a time. Tap to go to the next template. Long press to reload the current template. diff --git a/apps/hassio/hassio.app.js b/apps/hassio/hassio.app.js new file mode 100644 index 000000000..55268a0a0 --- /dev/null +++ b/apps/hassio/hassio.app.js @@ -0,0 +1,132 @@ +Modules.addCached("Layout",function(){function p(d,h){function b(e){"ram";e.id&&(a[e.id]=e);e.type||(e.type="");e.c&&e.c.forEach(b)}this._l=this.l=d;this.options=h||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let f;if(2!=process.env.HWVERSION){this.physBtns=3;f=[];function e(l){"ram";"btn"==l.type&&f.push(l);l.c&&l.c.forEach(e)}e(d);f.length&&(this.physBtns=0,this.buttons=f,this.selectedButton=-1)}if(this.options.btns)if(d=this.options.btns,this.physBtns>=d.length){this.b=d;let e=Math.floor(Bangle.appRect.h/ +this.physBtns);for(2d.length;)d.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:d.map(l=>(l.type="txt",l.font="6x8",l.height=e,l.r=1,l))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:d.map(e=>(e.type="btn",e.filly=1,e.width=32,e.r=1,e))}]},f&&f.push.apply(f,this._l.c[1].c);this.setUI();var a=this;b(this._l);this.updateNeeded=!0}function t(d, +h,b,f,a){var e=null==d.bgCol?a:g.toColor(d.bgCol);if(e!=a||"txt"==d.type||"btn"==d.type||"img"==d.type||"custom"==d.type){var l=d.c;delete d.c;var k="H"+E.CRC32(E.toJS(d));l&&(d.c=l);delete h[k]||((f[k]=[d.x,d.y,d.x+d.w-1,d.y+d.h-1]).bg=null==a?g.theme.bg:a,b&&(b.push(d),b=null))}if(d.c)for(var c of d.c)t(c,h,b,f,e)}p.prototype.setUI=function(){Bangle.setUI();let d;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back,remove:this.options.remove},h=>{var b=this.selectedButton,f=this.buttons.length; +if(void 0===h&&this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);!this.options.back&&!this.options.remove||d||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function h(b,f){.75=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler= +(b,f)=>h(this._l,f);Bangle.on("touch",Bangle.touchHandler)}};p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>> +1),n)}else b.setFont(c.font).setFontAlign(0,0,c.r).drawString(c.label,c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=void 0!==c.btnBorderCol?c.btnBorderCol:b.theme.fg2;q=void 0!==c.btnFaceCol?c.btnFaceCol:b.theme.bg2;c.selected&&(q=b.theme.bgH,n=b.theme.fgH);b.setColor(q).fillPoly(m).setColor(n).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src? +b.setBgColor(q).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)}):b.setFont(c.font||"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects|| +(this.rects={});var a=this.rects.clone(),e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d=Object.keys(a).map(c=>a[c]).reverse();for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w- +b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h-(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<<1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram";var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};if(h[d.type])h[d.type](d)};p.prototype.debug= +function(d,h){d||(d=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h);d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()* +a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label);a._w=e.width;a._h=e.height}},btn:function(a){"ram";a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width* +l;a._h=e.height*l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&& +(a.filly=1)}},f=this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p +}); + +if (HASSIO === undefined) { + loadHassio(); +} + +function noHassio() { + let Layout = require('Layout'); + let layout = new Layout( { + type:"v", c: [ + { + type: "txt", + font: "10%", + label: "No settings", + }, + ], + halign: -1 + }); + g.clear(); + layout.render(); +} + +const templateGui = () => { + if (HASSIO === undefined) { + noHassio(); + return; + } + + let selectedTemplate = 0; + + let Layout = require('Layout'); + let layout = new Layout( { + type:"v", c: [ + { + type: "txt", + font: "8%", + id: "name", + label: HASSIO.templates[selectedTemplate].name, + }, + { + type: 'txt', + font:"10%", + id: "data", + label: "data", + wrap: true, + width: g.getWidth(), + height: g.getHeight()-80, + halign: -1 + }, + { + type: "txt", + font: "8%", + id: "loc", + label: (selectedTemplate+1) + "/" + HASSIO.templates.length, + } + ], + halign: -1 + }); + + const fetchTemplate = (template) => { + const url = `${HASSIO.host}/api/template`; + return Bangle.http(url, { + method: "POST", + timeout: 2000, + body: JSON.stringify({template: template}), + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${HASSIO.api_key}`, + } + }); + }; + + const draw = (selected) => { + setTimeout(() => { + if (selected != selectedTemplate) + return; + + fetchTemplate(HASSIO.templates[selectedTemplate].temp).then((data) => { + if (selected != selectedTemplate) + return; + + layout.data.label = data.resp; + layout.clear(layout.data); + layout.render(); + }, (data) => { + if (selected != selectedTemplate) + return; + + layout.data.label = "failed " + JSON.stringify(data); + layout.clear(layout.data); + layout.render(); + }); + }, 1000); + }; + + Bangle.on('touch', function(button, xy) { + if (xy.type === 0) { + selectedTemplate++; + if (selectedTemplate >= HASSIO.templates.length) + selectedTemplate = 0; + layout.loc.label = (selectedTemplate+1) + "/" + HASSIO.templates.length; + layout.name.label = HASSIO.templates[selectedTemplate].name + } + + layout.data.label = "loading"; + g.clear(); + layout.render(); + draw(selectedTemplate) + }); + + + g.clear(); + layout.data.label = "loading"; + layout.render(); + draw(selectedTemplate); +}; + +templateGui(); \ No newline at end of file diff --git a/apps/hassio/hassio.boot.js b/apps/hassio/hassio.boot.js new file mode 100644 index 000000000..4bd84cadc --- /dev/null +++ b/apps/hassio/hassio.boot.js @@ -0,0 +1,128 @@ +var HASSIO; +let hassioRunning = false; + +function validateHassio(settings) { + const STR_FIELDS = ["api_key", "host", "id", "friendly_name"]; + const INT_FIELDS = ["interval"]; + const TEMPLATES = "templates"; + + if (typeof settings !== "object") { + return false; + } + + for (const field of STR_FIELDS) { + if (settings[field] === undefined || typeof settings[field] !== "string") { + return false; + } + } + + for (const field of INT_FIELDS) { + if (settings[field] === undefined || typeof settings[field] !== "number") { + return false; + } + } + if (settings[TEMPLATES] === undefined || !(settings[TEMPLATES] instanceof Array)) { + return false; + } + for (const template of settings[TEMPLATES]) { + if (template.name === undefined || typeof template.name !== "string") { + return false; + } + if (template.temp === undefined || typeof template.temp !== "string") { + return false; + } + } + return true; +} + +const loadHassio = () => { + let hassioSettings = require("Storage").read("hassio.json"); + let tmp = HASSIO; + HASSIO = undefined; + if (hassioSettings !== undefined) { + try { + HASSIO = JSON.parse(hassioSettings); + } catch(e) { + } + + if (HASSIO !== undefined && !validateHassio(HASSIO)) { + HASSIO = undefined; + } + } + if (HASSIO === undefined) { + HASSIO = tmp; + } +}; + +loadHassio(); + +const runHassio = () => { + if (HASSIO !== undefined) { + let hassioAttributes = { + state_class: "measurement", + friendly_name: HASSIO.friendly_name, + unit_of_measurement: "%" + }; + + const postSensor = (data) => { + const url = `${HASSIO.host}/api/states/sensor.${HASSIO.id}`; + Bangle.http(url, { + method: "POST", + body: JSON.stringify(data), + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${HASSIO.api_key}`, + } + }); + }; + + const getBattery = () => { + const b = E.getBattery(), + c = Bangle.isCharging(); + let i = "mdi:battery"; + if (c) i += "-charging"; + + return { + state: c ? "charging" : "discharging", + level: b + }; + }; + + Bangle.on("GPS", (fix) => { + hassioAttributes.gps = fix; + }); + + const updateSensor = () => { + hassioAttributes.health = Bangle.getHealthStatus(); + hassioAttributes.accel = Bangle.getAccel(); + hassioAttributes.battery = getBattery(); + hassioAttributes.compass = Bangle.getCompass(); + + postSensor({ + state: hassioAttributes.battery.level, + attributes: hassioAttributes + }); + }; + + const log = () => { + Bangle.setCompassPower(true, "hassio"); + + setTimeout(() => { + updateSensor(); + Bangle.setCompassPower(false, "hassio"); + }, 30 * 1000); + }; + + log(); + } +} + +(function () { + if (!hassioRunning) { + hassioRunning = true; + setTimeout(() => { + runHassio(); + setInterval(runHassio, HASSIO.interval); + }, 5000); + } +})(); \ No newline at end of file diff --git a/apps/hassio/hassio.img b/apps/hassio/hassio.img new file mode 100644 index 0000000000000000000000000000000000000000..49fea5b50a3e6134121302f511b9285a1bfaa492 GIT binary patch literal 292 zcma*hy9vWU3WP(Jg?L3hl0~6)LN$kSpia9qhd8iDtK-~XJJiilP_@P=G literal 0 HcmV?d00001 diff --git a/apps/hassio/hassio.json b/apps/hassio/hassio.json new file mode 100644 index 000000000..5f79da858 --- /dev/null +++ b/apps/hassio/hassio.json @@ -0,0 +1,13 @@ +{ + "templates": [ + { + "name":"Test Template", + "temp":"{% for state in states.weather -%}{%- if loop.first %}The {% elif loop.last %} and the {% else %}, the {% endif -%}{{ state.name | lower }} is {{state.state_with_unit}}{%- endfor %}." + } + ], + "interval": 180000, + "api_key":"api_key", + "host":"https://homeassistant:8123", + "id":"banglejs", + "friendly_name":"Banglejs Sensors" +} \ No newline at end of file diff --git a/apps/hassio/hassio.png b/apps/hassio/hassio.png new file mode 100644 index 0000000000000000000000000000000000000000..8fce958e47d8f0a5ba2fda55f346b052ffe3ca8c GIT binary patch literal 980 zcmV;_11tQAP)xlBv|z+t{{eBO8xc2((3QpC3l~MvgMq`NFd+=C%{}d}j+-u+%$1C_VNye|Z+4xLWaMBJ8r*2_WCOa;idWB6dCk7HuyEnPMg_rQM zjbU*OALA{Y#WHTU{;N0fs1#CPhi(?PVL$f8nj4$6uP4gF<}!{+_dtdgj>cH=Bwoke z_#8ine1SWLX?$7Ov~eQlG;uhj8M+fOC%@(|>7*8Ox=T1TEaQ7sn;)c5famZ#e#5h& zn~gceb!$?%4Bdf#)#j6!)5J$P{fD6&k2(F4ys`tkY8XE$CF}E;D4CNbU>jdyHRQ9< z?Ta~Ejw+W_mXyb1%@m#rnF`%;)ynMx?irNvbuOt6&P$0~#NimL!qLW?25{Nk$|cpo zmzd7+OiQJ)4#%c1yQ*PRB?%9Ot}ajC-f65vKaSU=gl!haR`DIaksd@3nzSmHTV)PO3hc+ zyPT6QurYj)BYi(~6EUZs19f=XVZ+5(zaUk!yE5XuCs*-|bVM2HLA)n*hAktS*gPsdmzq>W#@Ik1Lb3Y#@T4-epo)XU7{O6cE?&+GUe zkMwcie9TqCb0qf4Y`P--^0$Oy@|~4Vr3|xpM~dM)3p^L_uJo`e!@=02tQgcr(SaM% z2^^K;@IEQ&@_&#Wz!^8^z?m4!unQ03{@^+m+yiZ^%Wlvm>E*PDA2C;0H)Z1mxW{@! zmtjt-NQ=SM?cN!h14UWr;zTdZ#hSV;$56giq!^nZ{#=qWye(%_<8*E5bp$ZzY zW-X^16i?Sly)Y(~z_FSeuS&iA>VSDh7-59}8vX%}nJ8(wgaF|H0000 + + + + + +

Config

+ + + + + + + + + + diff --git a/apps/hassio/metadata.json b/apps/hassio/metadata.json new file mode 100644 index 000000000..14b4a5cc2 --- /dev/null +++ b/apps/hassio/metadata.json @@ -0,0 +1,19 @@ +{ "id": "hassio", + "name": "Home Assistant API Interface", + "shortName":"Hassio", + "icon": "hassio.png", + "version":"0.01", + "description": "This app gives access to viewing Home Assistant data and sends health, compass, accelerometer, and battery information to Home Assistant as a sensor through the Home Assistant REST API.", + "tags": "tool,sensors", + "supports": ["BANGLEJS2"], + "dependencies": {"android":"app"}, + "interface": "interface.html", + "readme": "README.md", + "storage": [ + {"name":"hassio.app.js","url":"hassio.app.js"}, + {"name":"hassio.boot.js","url":"hassio.boot.js"}, + {"name":"hassio.img","url":"hassio.img"}, + {"name":"hassio.json","url":"hassio.json"} + ] + +} \ No newline at end of file From bcbbf17af7c47a0d9af1207f8407909e89837bea Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Sat, 9 Mar 2024 15:59:19 +1100 Subject: [PATCH 02/23] fix: hassio changlog --- apps/hassio/ChangeLog | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/hassio/ChangeLog diff --git a/apps/hassio/ChangeLog b/apps/hassio/ChangeLog new file mode 100644 index 000000000..2286a7f70 --- /dev/null +++ b/apps/hassio/ChangeLog @@ -0,0 +1 @@ +0.01: New App! \ No newline at end of file From 2d2896e179ee2d4dbe8a1355e8f6e29092ce005a Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:11:12 +1100 Subject: [PATCH 03/23] fix: typo --- apps/hassio/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/hassio/README.md b/apps/hassio/README.md index 70703c3fb..7416bd90a 100644 --- a/apps/hassio/README.md +++ b/apps/hassio/README.md @@ -2,7 +2,7 @@ This app provides two features: -- Sending health, compass, accelerometer, and battery information to [Home Assistant](https://www.home-assistant.io/) +- Sending health, compass, accelerator, and battery information to [Home Assistant](https://www.home-assistant.io/) - Displaying [Home Assistant](https://www.home-assistant.io/) templates This is done through rest api calls to your [Home Assistant](https://www.home-assistant.io/) server. This means the app requires using the [Android Integration](/?id=android) and for your server to be accessible from your phone. From 8fd67340d41851b57d208fd2472fd471779bdd47 Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Sat, 9 Mar 2024 20:26:41 +1100 Subject: [PATCH 04/23] fix: ensure hrm value --- apps/hassio/hassio.boot.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/hassio/hassio.boot.js b/apps/hassio/hassio.boot.js index 4bd84cadc..e9a203131 100644 --- a/apps/hassio/hassio.boot.js +++ b/apps/hassio/hassio.boot.js @@ -106,10 +106,12 @@ const runHassio = () => { const log = () => { Bangle.setCompassPower(true, "hassio"); + Bangle.setHRMPower(true, "hassio"); setTimeout(() => { updateSensor(); Bangle.setCompassPower(false, "hassio"); + Bangle.setHRMPower(false, "hassio"); }, 30 * 1000); }; From a92d27e3b0f35a7ddf9f39ef15622a0fce017633 Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Sat, 9 Mar 2024 21:42:00 +1100 Subject: [PATCH 05/23] fix: semi colon --- apps/hassio/hassio.boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/hassio/hassio.boot.js b/apps/hassio/hassio.boot.js index e9a203131..ff03e5d36 100644 --- a/apps/hassio/hassio.boot.js +++ b/apps/hassio/hassio.boot.js @@ -117,7 +117,7 @@ const runHassio = () => { log(); } -} +}; (function () { if (!hassioRunning) { From a667fc8061886ca56db6b5e8586cb1719da5e30d Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 08:24:45 +0000 Subject: [PATCH 06/23] clock_info: factor out focus/defocus --- apps/clock_info/lib.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index e6c9eb27f..a35d7074e 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -284,38 +284,39 @@ exports.addInteractive = function(menu, options) { E.stopEventPropagation&&E.stopEventPropagation(); } Bangle.on("swipe",swipeHandler); + const unfocus = () => { + options.focus=false; + delete Bangle.CLKINFO_FOCUS; + options.redraw(); + }; + const focus = (redraw) => { + options.focus=true; + Bangle.CLKINFO_FOCUS=true; + if (redraw) options.redraw(); + }; let touchHandler, lockHandler; if (options.x!==undefined && options.y!==undefined && options.w && options.h) { touchHandler = function(_,e) { if (e.x(options.x+options.w) || e.y>(options.y+options.h)) { - if (options.focus) { - options.focus=false; - delete Bangle.CLKINFO_FOCUS; - options.redraw(); - } + if (options.focus) + unfocus(); return; // outside area } if (!options.focus) { - options.focus=true; // if not focussed, set focus - Bangle.CLKINFO_FOCUS=true; - options.redraw(); + focus(true); } else if (menu[options.menuA].items[options.menuB].run) { Bangle.buzz(100, 0.7); menu[options.menuA].items[options.menuB].run(); // allow tap on an item to run it (eg home assistant) } else { - options.focus=true; - Bangle.CLKINFO_FOCUS=true; + focus(); } }; Bangle.on("touch",touchHandler); if (settings.defocusOnLock) { lockHandler = function() { - if(options.focus) { - options.focus=false; - delete Bangle.CLKINFO_FOCUS; - options.redraw(); - } + if(options.focus) + unfocus(); }; Bangle.on("lock", lockHandler); } From 30d1bc7c4c3e0b0241ba7e89c4e7ba82590835ee Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 08:31:13 +0000 Subject: [PATCH 07/23] clock_info: pass options through to show(), hide() & run() --- apps/clock_info/lib.js | 6 +++--- typescript/types/clock_info.d.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index a35d7074e..173b44e5d 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -234,7 +234,7 @@ exports.addInteractive = function(menu, options) { options.redrawHandler = ()=>drawItem(itm); itm.on('redraw', options.redrawHandler); itm.uses = (0|itm.uses)+1; - if (itm.uses==1) itm.show(); + if (itm.uses==1) itm.show(options); itm.emit("redraw"); } function menuHideItem(itm) { @@ -242,7 +242,7 @@ exports.addInteractive = function(menu, options) { delete options.redrawHandler; itm.uses--; if (!itm.uses) - itm.hide(); + itm.hide(options); } // handling for swipe between menu items function swipeHandler(lr,ud){ @@ -307,7 +307,7 @@ exports.addInteractive = function(menu, options) { focus(true); } else if (menu[options.menuA].items[options.menuB].run) { Bangle.buzz(100, 0.7); - menu[options.menuA].items[options.menuB].run(); // allow tap on an item to run it (eg home assistant) + menu[options.menuA].items[options.menuB].run(options); // allow tap on an item to run it (eg home assistant) } else { focus(); } diff --git a/typescript/types/clock_info.d.ts b/typescript/types/clock_info.d.ts index b12732683..fbdb3354c 100644 --- a/typescript/types/clock_info.d.ts +++ b/typescript/types/clock_info.d.ts @@ -11,10 +11,10 @@ declare module ClockInfo { type MenuItem = { name: string, - show(): void, - hide(): void, + show(options: InteractiveOptions): void, + hide(options: InteractiveOptions): void, on(what: "redraw", cb: () => void): void, // extending from Object - run?(): void, + run?(options: InteractiveOptions): void, } & ( { hasRange: true, From 8f86d9297b1d51c00d57f40514129d1e4cbb4941 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 08:42:45 +0000 Subject: [PATCH 08/23] clock_info: add focus and unfocus to item --- apps/clock_info/lib.js | 10 ++++++++-- typescript/types/clock_info.d.ts | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 173b44e5d..3799dcb73 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -288,10 +288,16 @@ exports.addInteractive = function(menu, options) { options.focus=false; delete Bangle.CLKINFO_FOCUS; options.redraw(); + const itm = menu[options.menuA].items[options.menuB]; + if (itm.unfocus) itm.unfocus(options); }; const focus = (redraw) => { - options.focus=true; - Bangle.CLKINFO_FOCUS=true; + if (!options.focus) { + options.focus=true; + Bangle.CLKINFO_FOCUS=true; + const itm = menu[options.menuA].items[options.menuB]; + if (itm.focus) itm.focus(options); + } if (redraw) options.redraw(); }; let touchHandler, lockHandler; diff --git a/typescript/types/clock_info.d.ts b/typescript/types/clock_info.d.ts index fbdb3354c..ea62e5602 100644 --- a/typescript/types/clock_info.d.ts +++ b/typescript/types/clock_info.d.ts @@ -15,6 +15,8 @@ declare module ClockInfo { hide(options: InteractiveOptions): void, on(what: "redraw", cb: () => void): void, // extending from Object run?(options: InteractiveOptions): void, + focus?(options: InteractiveOptions): void, + unfocus?(options: InteractiveOptions): void, } & ( { hasRange: true, From 2c92c9457416ac0f61e68d957a7d1a277ae57692 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 08:43:20 +0000 Subject: [PATCH 09/23] clock_info: s/unfocus/blur/ --- apps/clock_info/lib.js | 8 ++++---- typescript/types/clock_info.d.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 3799dcb73..6581a9469 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -284,12 +284,12 @@ exports.addInteractive = function(menu, options) { E.stopEventPropagation&&E.stopEventPropagation(); } Bangle.on("swipe",swipeHandler); - const unfocus = () => { + const blur = () => { options.focus=false; delete Bangle.CLKINFO_FOCUS; options.redraw(); const itm = menu[options.menuA].items[options.menuB]; - if (itm.unfocus) itm.unfocus(options); + if (itm.blur) itm.blur(options); }; const focus = (redraw) => { if (!options.focus) { @@ -306,7 +306,7 @@ exports.addInteractive = function(menu, options) { if (e.x(options.x+options.w) || e.y>(options.y+options.h)) { if (options.focus) - unfocus(); + blur(); return; // outside area } if (!options.focus) { @@ -322,7 +322,7 @@ exports.addInteractive = function(menu, options) { if (settings.defocusOnLock) { lockHandler = function() { if(options.focus) - unfocus(); + blur(); }; Bangle.on("lock", lockHandler); } diff --git a/typescript/types/clock_info.d.ts b/typescript/types/clock_info.d.ts index ea62e5602..91b8d5b49 100644 --- a/typescript/types/clock_info.d.ts +++ b/typescript/types/clock_info.d.ts @@ -16,7 +16,7 @@ declare module ClockInfo { on(what: "redraw", cb: () => void): void, // extending from Object run?(options: InteractiveOptions): void, focus?(options: InteractiveOptions): void, - unfocus?(options: InteractiveOptions): void, + blur?(options: InteractiveOptions): void, } & ( { hasRange: true, From 33088c625742ca8f35d31df3693ae5355e171a7b Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 08:57:36 +0000 Subject: [PATCH 10/23] clock_info: call focus() after redraw --- apps/clock_info/lib.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 6581a9469..513d946ac 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -292,13 +292,17 @@ exports.addInteractive = function(menu, options) { if (itm.blur) itm.blur(options); }; const focus = (redraw) => { + let shown = false; if (!options.focus) { options.focus=true; Bangle.CLKINFO_FOCUS=true; + shown = true; + } + if (redraw) options.redraw(); + if (shown) { const itm = menu[options.menuA].items[options.menuB]; if (itm.focus) itm.focus(options); } - if (redraw) options.redraw(); }; let touchHandler, lockHandler; if (options.x!==undefined && options.y!==undefined && options.w && options.h) { From 54907714a2cca62ff77013b1c576d6575141a483 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 3 Mar 2024 23:04:36 +0000 Subject: [PATCH 11/23] smpltmr: reset clkinfo to "main" timer on blur --- apps/smpltmr/ChangeLog | 3 ++- apps/smpltmr/clkinfo.js | 8 +++++++- apps/smpltmr/metadata.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/smpltmr/ChangeLog b/apps/smpltmr/ChangeLog index dcc5021b5..2c073ff43 100644 --- a/apps/smpltmr/ChangeLog +++ b/apps/smpltmr/ChangeLog @@ -5,4 +5,5 @@ 0.05: Updated clkinfo icon. 0.06: Ensure Timer supplies an image for clkinfo items 0.07: Update clock_info to avoid a redraw -0.08: Timer ClockInfo now updates once a minute \ No newline at end of file +0.08: Timer ClockInfo now updates once a minute +0.09: Timer ClockInfo resets to timer menu when blurred diff --git a/apps/smpltmr/clkinfo.js b/apps/smpltmr/clkinfo.js index d68372f15..a7a6bf71b 100644 --- a/apps/smpltmr/clkinfo.js +++ b/apps/smpltmr/clkinfo.js @@ -71,13 +71,19 @@ ] }; + const restoreMainItem = function(clkinfo) { + clkinfo.menuB = 0; + // clock info redraws after this + }; + var offsets = [+5,-5]; offsets.forEach((o, i) => { smpltmrItems.items = smpltmrItems.items.concat({ name: null, get: () => ({ text: (o > 0 ? "+" : "") + o + " min.", img: smpltmrItems.img }), show: function() { }, - hide: function () { }, + hide: function() { }, + blur: restoreMainItem, run: function() { if(o > 0) increaseAlarm(o); else decreaseAlarm(Math.abs(o)); diff --git a/apps/smpltmr/metadata.json b/apps/smpltmr/metadata.json index db492b0c1..98affcfe6 100644 --- a/apps/smpltmr/metadata.json +++ b/apps/smpltmr/metadata.json @@ -2,7 +2,7 @@ "id": "smpltmr", "name": "Simple Timer", "shortName": "Simple Timer", - "version": "0.08", + "version": "0.09", "description": "A very simple app to start a timer.", "icon": "app.png", "tags": "tool,alarm,timer,clkinfo", From 9c16fa6c79c90adc6d2298015d5312280a5dc881 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 08:52:14 +0000 Subject: [PATCH 12/23] clock_info: bump version --- apps/clock_info/ChangeLog | 1 + apps/clock_info/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/clock_info/ChangeLog b/apps/clock_info/ChangeLog index 870808eff..97e62e238 100644 --- a/apps/clock_info/ChangeLog +++ b/apps/clock_info/ChangeLog @@ -6,3 +6,4 @@ 0.05: Reported image for battery is now transparent (2v18+) 0.06: When >1 clockinfo, swiping one back tries to ensure they don't display the same thing 0.07: Developer tweak: clkinfo load errors are emitted +0.08: Pass options to show(), hide() and run(), and add focus() and blur() item methods diff --git a/apps/clock_info/metadata.json b/apps/clock_info/metadata.json index 993f112e7..351425a8f 100644 --- a/apps/clock_info/metadata.json +++ b/apps/clock_info/metadata.json @@ -1,7 +1,7 @@ { "id": "clock_info", "name": "Clock Info Module", "shortName": "Clock Info", - "version":"0.07", + "version":"0.08", "description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)", "icon": "app.png", "type": "module", From a391f9a1b33225672262155c8fa12426c096afb0 Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:39:21 +1100 Subject: [PATCH 13/23] fix: remove inlined module code --- apps/hassio/hassio.app.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/apps/hassio/hassio.app.js b/apps/hassio/hassio.app.js index 55268a0a0..f12393773 100644 --- a/apps/hassio/hassio.app.js +++ b/apps/hassio/hassio.app.js @@ -1,19 +1,3 @@ -Modules.addCached("Layout",function(){function p(d,h){function b(e){"ram";e.id&&(a[e.id]=e);e.type||(e.type="");e.c&&e.c.forEach(b)}this._l=this.l=d;this.options=h||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let f;if(2!=process.env.HWVERSION){this.physBtns=3;f=[];function e(l){"ram";"btn"==l.type&&f.push(l);l.c&&l.c.forEach(e)}e(d);f.length&&(this.physBtns=0,this.buttons=f,this.selectedButton=-1)}if(this.options.btns)if(d=this.options.btns,this.physBtns>=d.length){this.b=d;let e=Math.floor(Bangle.appRect.h/ -this.physBtns);for(2d.length;)d.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:d.map(l=>(l.type="txt",l.font="6x8",l.height=e,l.r=1,l))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:d.map(e=>(e.type="btn",e.filly=1,e.width=32,e.r=1,e))}]},f&&f.push.apply(f,this._l.c[1].c);this.setUI();var a=this;b(this._l);this.updateNeeded=!0}function t(d, -h,b,f,a){var e=null==d.bgCol?a:g.toColor(d.bgCol);if(e!=a||"txt"==d.type||"btn"==d.type||"img"==d.type||"custom"==d.type){var l=d.c;delete d.c;var k="H"+E.CRC32(E.toJS(d));l&&(d.c=l);delete h[k]||((f[k]=[d.x,d.y,d.x+d.w-1,d.y+d.h-1]).bg=null==a?g.theme.bg:a,b&&(b.push(d),b=null))}if(d.c)for(var c of d.c)t(c,h,b,f,e)}p.prototype.setUI=function(){Bangle.setUI();let d;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back,remove:this.options.remove},h=>{var b=this.selectedButton,f=this.buttons.length; -if(void 0===h&&this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);!this.options.back&&!this.options.remove||d||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function h(b,f){.75=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler= -(b,f)=>h(this._l,f);Bangle.on("touch",Bangle.touchHandler)}};p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>> -1),n)}else b.setFont(c.font).setFontAlign(0,0,c.r).drawString(c.label,c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=void 0!==c.btnBorderCol?c.btnBorderCol:b.theme.fg2;q=void 0!==c.btnFaceCol?c.btnFaceCol:b.theme.bg2;c.selected&&(q=b.theme.bgH,n=b.theme.fgH);b.setColor(q).fillPoly(m).setColor(n).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src? -b.setBgColor(q).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)}):b.setFont(c.font||"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects|| -(this.rects={});var a=this.rects.clone(),e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d=Object.keys(a).map(c=>a[c]).reverse();for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w- -b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h-(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<<1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram";var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};if(h[d.type])h[d.type](d)};p.prototype.debug= -function(d,h){d||(d=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h);d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()* -a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label);a._w=e.width;a._h=e.height}},btn:function(a){"ram";a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width* -l;a._h=e.height*l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&& -(a.filly=1)}},f=this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p -}); - if (HASSIO === undefined) { loadHassio(); } From 04d838dcccd04e0faafffc163d19c507743a9699 Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:40:20 +1100 Subject: [PATCH 14/23] fix: remove unused variable --- apps/hassio/hassio.boot.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/hassio/hassio.boot.js b/apps/hassio/hassio.boot.js index ff03e5d36..14aa34f6a 100644 --- a/apps/hassio/hassio.boot.js +++ b/apps/hassio/hassio.boot.js @@ -79,8 +79,6 @@ const runHassio = () => { const getBattery = () => { const b = E.getBattery(), c = Bangle.isCharging(); - let i = "mdi:battery"; - if (c) i += "-charging"; return { state: c ? "charging" : "discharging", From 3e22a9690a8161ec913b7859fdb1cc7b98d91b35 Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:46:30 +1100 Subject: [PATCH 15/23] refactor: use readStorageJSON function --- apps/hassio/interface.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/hassio/interface.html b/apps/hassio/interface.html index d2ccc83aa..0b9f4a955 100644 --- a/apps/hassio/interface.html +++ b/apps/hassio/interface.html @@ -70,11 +70,11 @@ function getData() { // show loading window Util.showModal("Loading..."); - Util.readStorage('hassio.json', data=>{ + Util.readStorageJSON('hassio.json', data=>{ // remove window Util.hideModal(); - document.getElementById('json').value = JSON.stringify(JSON.parse(data), null, 1); + document.getElementById('json').value = JSON.stringify(data, null, 1); }); } From 154c54f9415e0f4401fd694fc60fc7a7820a5b5d Mon Sep 17 00:00:00 2001 From: ellabellla <86757313+ellabellla@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:48:36 +1100 Subject: [PATCH 16/23] fix: move settings to data --- apps/hassio/metadata.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/hassio/metadata.json b/apps/hassio/metadata.json index 14b4a5cc2..0e9c6a2d5 100644 --- a/apps/hassio/metadata.json +++ b/apps/hassio/metadata.json @@ -12,8 +12,7 @@ "storage": [ {"name":"hassio.app.js","url":"hassio.app.js"}, {"name":"hassio.boot.js","url":"hassio.boot.js"}, - {"name":"hassio.img","url":"hassio.img"}, - {"name":"hassio.json","url":"hassio.json"} - ] - + {"name":"hassio.img","url":"hassio.img"} + ], + "data": [{"name":"hassio.json"}] } \ No newline at end of file From 736b9f0433ad76d2867bd2d37401c1cc382d5b42 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 08:58:41 +0000 Subject: [PATCH 17/23] clock_info: permit focus/blur to cancel a redraw --- apps/clock_info/lib.js | 15 +++++++-------- typescript/types/clock_info.d.ts | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 513d946ac..ff7340ec0 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -287,22 +287,21 @@ exports.addInteractive = function(menu, options) { const blur = () => { options.focus=false; delete Bangle.CLKINFO_FOCUS; - options.redraw(); const itm = menu[options.menuA].items[options.menuB]; - if (itm.blur) itm.blur(options); + let redraw = true; + if (itm.blur && itm.blur(options) === false) + redraw = false; + if (redraw) options.redraw(); }; const focus = (redraw) => { - let shown = false; if (!options.focus) { options.focus=true; Bangle.CLKINFO_FOCUS=true; - shown = true; + const itm = menu[options.menuA].items[options.menuB]; + if (itm.focus && itm.focus(options) === false) + redraw = false; } if (redraw) options.redraw(); - if (shown) { - const itm = menu[options.menuA].items[options.menuB]; - if (itm.focus) itm.focus(options); - } }; let touchHandler, lockHandler; if (options.x!==undefined && options.y!==undefined && options.w && options.h) { diff --git a/typescript/types/clock_info.d.ts b/typescript/types/clock_info.d.ts index 91b8d5b49..7f48cbf0a 100644 --- a/typescript/types/clock_info.d.ts +++ b/typescript/types/clock_info.d.ts @@ -15,8 +15,8 @@ declare module ClockInfo { hide(options: InteractiveOptions): void, on(what: "redraw", cb: () => void): void, // extending from Object run?(options: InteractiveOptions): void, - focus?(options: InteractiveOptions): void, - blur?(options: InteractiveOptions): void, + focus?(options: InteractiveOptions): void | false, + blur?(options: InteractiveOptions): void | false, } & ( { hasRange: true, From 70620c1dabf8c48c99ba8201782d83958d7eb645 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 11 Mar 2024 21:52:32 +0000 Subject: [PATCH 18/23] clock_info: Update README with show/hide arg and focus+blur --- apps/clock_info/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/clock_info/README.md b/apps/clock_info/README.md index 7e1a3d637..031f89121 100644 --- a/apps/clock_info/README.md +++ b/apps/clock_info/README.md @@ -70,10 +70,12 @@ Note that each item is an object with: } ``` -* `item.show` : called when item should be shown. Enables updates. Call BEFORE 'get' -* `item.hide` : called when item should be hidden. Disables updates. +* `item.show` : called when item should be shown. Enables updates. Call BEFORE 'get'. Passed the clockinfo options (same as what's returned from `addInteractive`). +* `item.hide` : called when item should be hidden. Disables updates. Passed the clockinfo options. * `.on('redraw', ...)` : event that is called when 'get' should be called again (only after 'item.show') * `item.run` : (optional) called if the info screen is tapped - can perform some action. Return true if the caller should feedback the user. +* `item.focus` : called when the item is focussed (the user has tapped on it). Passed the clockinfo options. +* `item.blur` : called when the item is unfocussed (the user has tapped elsewhere, the screen has locked, etc). Passed the clockinfo options. See the bottom of `lib.js` for example usage... From 7005bd89050e451ca741fe0a26958189a8652d87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 00:40:23 +0000 Subject: [PATCH 19/23] build(deps): bump core from `bd301be` to `0222d3c` Bumps [core](https://github.com/espruino/EspruinoAppLoaderCore) from `bd301be` to `0222d3c`. - [Commits](https://github.com/espruino/EspruinoAppLoaderCore/compare/bd301be3324775a8f464328ba9e34f750d503a2b...0222d3c5ac608a1b842ffc1f1f79e19276d648fe) --- 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 bd301be33..0222d3c5a 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit bd301be3324775a8f464328ba9e34f750d503a2b +Subproject commit 0222d3c5ac608a1b842ffc1f1f79e19276d648fe From 48478a586eb6a71f33f731476e257c20beccead8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 00:40:25 +0000 Subject: [PATCH 20/23] build(deps): bump webtools from `af870d7` to `8d671ad` Bumps [webtools](https://github.com/espruino/EspruinoWebTools) from `af870d7` to `8d671ad`. - [Commits](https://github.com/espruino/EspruinoWebTools/compare/af870d7b8386bfa824b07b268bce414e4daf3fbb...8d671ad0dfb1d5a36f4ee9952390f4d79019e61d) --- updated-dependencies: - dependency-name: webtools dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- webtools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webtools b/webtools index af870d7b8..8d671ad0d 160000 --- a/webtools +++ b/webtools @@ -1 +1 @@ -Subproject commit af870d7b8386bfa824b07b268bce414e4daf3fbb +Subproject commit 8d671ad0dfb1d5a36f4ee9952390f4d79019e61d From 37013751923e92c9cdd2a8b2b9de57833d36987a Mon Sep 17 00:00:00 2001 From: Kendell R Date: Mon, 11 Mar 2024 17:59:48 -0700 Subject: [PATCH 21/23] Update calculator README --- apps/calculator/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/calculator/README.md b/apps/calculator/README.md index 62f6cef24..29edd433a 100644 --- a/apps/calculator/README.md +++ b/apps/calculator/README.md @@ -20,9 +20,9 @@ Bangle.js 1 - SELECT: BTN2 Bangle.js 2 -- Swipes to change visible buttons -- Click physical button to exit -- Press upper left corner of screen to exit (where the red back button would be) +- Swipe up or down to go back to the number input +- Swipe to the left for operators, swipe to the right for the special functions +- Exit by pressing the physical button or the upper left corner of screen to exit (where the red back button would be) ## Creator From 1d6457ed19765a8011e18e00b813bf0639362166 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Tue, 12 Mar 2024 07:38:17 +0000 Subject: [PATCH 22/23] clock_info: set CLKINFO_FOCUS on start --- apps/clock_info/lib.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index ff7340ec0..79fbb849e 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -294,9 +294,9 @@ exports.addInteractive = function(menu, options) { if (redraw) options.redraw(); }; const focus = (redraw) => { + Bangle.CLKINFO_FOCUS=true; if (!options.focus) { options.focus=true; - Bangle.CLKINFO_FOCUS=true; const itm = menu[options.menuA].items[options.menuB]; if (itm.focus && itm.focus(options) === false) redraw = false; @@ -362,6 +362,7 @@ exports.addInteractive = function(menu, options) { return true; }; + if (options.focus) focus(); delete settings; // don't keep settings in RAM - save space return options; }; From 0943d20231d663a97442e2530d096aaf1ebf0a4a Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Tue, 12 Mar 2024 07:39:00 +0000 Subject: [PATCH 23/23] clock_info: remove redundant focus call --- apps/clock_info/lib.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 79fbb849e..d35ac5cf0 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -293,7 +293,8 @@ exports.addInteractive = function(menu, options) { redraw = false; if (redraw) options.redraw(); }; - const focus = (redraw) => { + const focus = () => { + let redraw = true; Bangle.CLKINFO_FOCUS=true; if (!options.focus) { options.focus=true; @@ -313,12 +314,10 @@ exports.addInteractive = function(menu, options) { return; // outside area } if (!options.focus) { - focus(true); + focus(); } else if (menu[options.menuA].items[options.menuB].run) { Bangle.buzz(100, 0.7); menu[options.menuA].items[options.menuB].run(options); // allow tap on an item to run it (eg home assistant) - } else { - focus(); } }; Bangle.on("touch",touchHandler);