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 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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