mirror of https://github.com/espruino/BangleApps
feat: hassio
parent
89e01a41de
commit
6614b93e95
|
@ -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.
|
|
@ -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(2<this.physBtns&&1==d.length&&d.unshift({label:""});this.physBtns>d.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<f.time-f.lastTime&&this.b[b].cbl?this.b[b].cbl(f):this.b[b].cb&&this.b[b].cb(f)}Bangle.btnWatches&&
|
||||
Bangle.btnWatches.forEach(clearWatch);Bangle.btnWatches=[];this.b[0]&&Bangle.btnWatches.push(setWatch(h.bind(this,0),BTN1,{repeat:!0,edge:-1}));this.b[1]&&Bangle.btnWatches.push(setWatch(h.bind(this,1),BTN2,{repeat:!0,edge:-1}));this.b[2]&&Bangle.btnWatches.push(setWatch(h.bind(this,2),BTN3,{repeat:!0,edge:-1}))}if(2==process.env.HWVERSION){function h(b,f){b.cb&&f.x>=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();
|
|
@ -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);
|
||||
}
|
||||
})();
|
Binary file not shown.
|
@ -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"
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 980 B |
|
@ -0,0 +1,106 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h4>Config</h4>
|
||||
<textarea style="width: 100%;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;height:80%;resize: none;" id="json">
|
||||
|
||||
</textarea>
|
||||
<button id="reset" class="btn btn-error">Reload</button>
|
||||
<button id="save" class="btn btn-primary">Save</button>
|
||||
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
|
||||
<script>
|
||||
const STR_FIELDS = ["api_key", "host", "id", "friendly_name"]
|
||||
const INT_FIELDS = ["interval"]
|
||||
const TEMPLATES = "templates"
|
||||
document.getElementById('reset').addEventListener('click', reset)
|
||||
document.getElementById('save').addEventListener('click', save);
|
||||
|
||||
function validate(str) {
|
||||
let settings = {};
|
||||
try {
|
||||
settings = JSON.parse(str)
|
||||
} catch (e) {
|
||||
alert("Unable to parse settings: " + String(e))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof settings !== "object") {
|
||||
alert("Settings must be an object")
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const field of STR_FIELDS) {
|
||||
if (settings[field] === undefined || typeof settings[field] !== "string") {
|
||||
alert(`Field ${field} must be a string`)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const field of INT_FIELDS) {
|
||||
if (settings[field] === undefined || typeof settings[field] !== "number") {
|
||||
alert(`Field ${field} must be a number`)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (settings[TEMPLATES] === undefined || !(settings[TEMPLATES] instanceof Array)) {
|
||||
alert(`Field ${TEMPLATES} must be a list`)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const template of settings[TEMPLATES]) {
|
||||
if (template["name"] === undefined || typeof template["name"] !== "string") {
|
||||
alert(`Field name of a template must be a string`)
|
||||
return false;
|
||||
}
|
||||
if (template["temp"] === undefined || typeof template["temp"] !== "string") {
|
||||
alert(`Field name of a template must be a string`)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getData() {
|
||||
// show loading window
|
||||
Util.showModal("Loading...");
|
||||
Util.readStorage('hassio.json', data=>{
|
||||
// remove window
|
||||
Util.hideModal();
|
||||
|
||||
document.getElementById('json').value = JSON.stringify(JSON.parse(data), null, 1);
|
||||
});
|
||||
}
|
||||
|
||||
function save(){
|
||||
Util.showModal("Saving...");
|
||||
let data = document.getElementById('json').value;
|
||||
|
||||
if (!validate(data)) {
|
||||
Util.hideModal();
|
||||
return;
|
||||
}
|
||||
|
||||
Util.writeStorage("hassio.json", data, () => {
|
||||
Util.hideModal();
|
||||
});
|
||||
}
|
||||
|
||||
function reset(){
|
||||
getData();
|
||||
}
|
||||
|
||||
// Called when app starts
|
||||
function onInit() {
|
||||
getData();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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"}
|
||||
]
|
||||
|
||||
}
|
Loading…
Reference in New Issue