Merge branch 'espruino:master' into master
commit
a7adcb9e53
|
|
@ -20,9 +20,9 @@ Bangle.js 1
|
||||||
- SELECT: BTN2
|
- SELECT: BTN2
|
||||||
|
|
||||||
Bangle.js 2
|
Bangle.js 2
|
||||||
- Swipes to change visible buttons
|
- Swipe up or down to go back to the number input
|
||||||
- Click physical button to exit
|
- Swipe to the left for operators, swipe to the right for the special functions
|
||||||
- Press upper left corner of screen to exit (where the red back button would be)
|
- Exit by pressing the physical button or the upper left corner of screen to exit (where the red back button would be)
|
||||||
## Creator
|
## Creator
|
||||||
|
|
||||||
<https://twitter.com/fredericrous>
|
<https://twitter.com/fredericrous>
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@
|
||||||
0.05: Reported image for battery is now transparent (2v18+)
|
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.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.07: Developer tweak: clkinfo load errors are emitted
|
||||||
|
0.08: Pass options to show(), hide() and run(), and add focus() and blur() item methods
|
||||||
|
|
|
||||||
|
|
@ -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.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.
|
* `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')
|
* `.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.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...
|
See the bottom of `lib.js` for example usage...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ exports.addInteractive = function(menu, options) {
|
||||||
options.redrawHandler = ()=>drawItem(itm);
|
options.redrawHandler = ()=>drawItem(itm);
|
||||||
itm.on('redraw', options.redrawHandler);
|
itm.on('redraw', options.redrawHandler);
|
||||||
itm.uses = (0|itm.uses)+1;
|
itm.uses = (0|itm.uses)+1;
|
||||||
if (itm.uses==1) itm.show();
|
if (itm.uses==1) itm.show(options);
|
||||||
itm.emit("redraw");
|
itm.emit("redraw");
|
||||||
}
|
}
|
||||||
function menuHideItem(itm) {
|
function menuHideItem(itm) {
|
||||||
|
|
@ -242,7 +242,7 @@ exports.addInteractive = function(menu, options) {
|
||||||
delete options.redrawHandler;
|
delete options.redrawHandler;
|
||||||
itm.uses--;
|
itm.uses--;
|
||||||
if (!itm.uses)
|
if (!itm.uses)
|
||||||
itm.hide();
|
itm.hide(options);
|
||||||
}
|
}
|
||||||
// handling for swipe between menu items
|
// handling for swipe between menu items
|
||||||
function swipeHandler(lr,ud){
|
function swipeHandler(lr,ud){
|
||||||
|
|
@ -284,38 +284,47 @@ exports.addInteractive = function(menu, options) {
|
||||||
E.stopEventPropagation&&E.stopEventPropagation();
|
E.stopEventPropagation&&E.stopEventPropagation();
|
||||||
}
|
}
|
||||||
Bangle.on("swipe",swipeHandler);
|
Bangle.on("swipe",swipeHandler);
|
||||||
|
const blur = () => {
|
||||||
|
options.focus=false;
|
||||||
|
delete Bangle.CLKINFO_FOCUS;
|
||||||
|
const itm = menu[options.menuA].items[options.menuB];
|
||||||
|
let redraw = true;
|
||||||
|
if (itm.blur && itm.blur(options) === false)
|
||||||
|
redraw = false;
|
||||||
|
if (redraw) options.redraw();
|
||||||
|
};
|
||||||
|
const focus = () => {
|
||||||
|
let redraw = true;
|
||||||
|
Bangle.CLKINFO_FOCUS=true;
|
||||||
|
if (!options.focus) {
|
||||||
|
options.focus=true;
|
||||||
|
const itm = menu[options.menuA].items[options.menuB];
|
||||||
|
if (itm.focus && itm.focus(options) === false)
|
||||||
|
redraw = false;
|
||||||
|
}
|
||||||
|
if (redraw) options.redraw();
|
||||||
|
};
|
||||||
let touchHandler, lockHandler;
|
let touchHandler, lockHandler;
|
||||||
if (options.x!==undefined && options.y!==undefined && options.w && options.h) {
|
if (options.x!==undefined && options.y!==undefined && options.w && options.h) {
|
||||||
touchHandler = function(_,e) {
|
touchHandler = function(_,e) {
|
||||||
if (e.x<options.x || e.y<options.y ||
|
if (e.x<options.x || e.y<options.y ||
|
||||||
e.x>(options.x+options.w) || e.y>(options.y+options.h)) {
|
e.x>(options.x+options.w) || e.y>(options.y+options.h)) {
|
||||||
if (options.focus) {
|
if (options.focus)
|
||||||
options.focus=false;
|
blur();
|
||||||
delete Bangle.CLKINFO_FOCUS;
|
|
||||||
options.redraw();
|
|
||||||
}
|
|
||||||
return; // outside area
|
return; // outside area
|
||||||
}
|
}
|
||||||
if (!options.focus) {
|
if (!options.focus) {
|
||||||
options.focus=true; // if not focussed, set focus
|
focus();
|
||||||
Bangle.CLKINFO_FOCUS=true;
|
|
||||||
options.redraw();
|
|
||||||
} else if (menu[options.menuA].items[options.menuB].run) {
|
} else if (menu[options.menuA].items[options.menuB].run) {
|
||||||
Bangle.buzz(100, 0.7);
|
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 {
|
|
||||||
options.focus=true;
|
|
||||||
Bangle.CLKINFO_FOCUS=true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Bangle.on("touch",touchHandler);
|
Bangle.on("touch",touchHandler);
|
||||||
if (settings.defocusOnLock) {
|
if (settings.defocusOnLock) {
|
||||||
lockHandler = function() {
|
lockHandler = function() {
|
||||||
if(options.focus) {
|
if(options.focus)
|
||||||
options.focus=false;
|
blur();
|
||||||
delete Bangle.CLKINFO_FOCUS;
|
|
||||||
options.redraw();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Bangle.on("lock", lockHandler);
|
Bangle.on("lock", lockHandler);
|
||||||
}
|
}
|
||||||
|
|
@ -352,6 +361,7 @@ exports.addInteractive = function(menu, options) {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
if (options.focus) focus();
|
||||||
delete settings; // don't keep settings in RAM - save space
|
delete settings; // don't keep settings in RAM - save space
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "clock_info",
|
{ "id": "clock_info",
|
||||||
"name": "Clock Info Module",
|
"name": "Clock Info Module",
|
||||||
"shortName": "Clock Info",
|
"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)",
|
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Home Assistant API Interface
|
||||||
|
|
||||||
|
This app provides two features:
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
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,116 @@
|
||||||
|
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();
|
||||||
|
|
||||||
|
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");
|
||||||
|
Bangle.setHRMPower(true, "hassio");
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
updateSensor();
|
||||||
|
Bangle.setCompassPower(false, "hassio");
|
||||||
|
Bangle.setHRMPower(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.readStorageJSON('hassio.json', data=>{
|
||||||
|
// remove window
|
||||||
|
Util.hideModal();
|
||||||
|
|
||||||
|
document.getElementById('json').value = JSON.stringify(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,18 @@
|
||||||
|
{ "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"}
|
||||||
|
],
|
||||||
|
"data": [{"name":"hassio.json"}]
|
||||||
|
}
|
||||||
|
|
@ -5,4 +5,5 @@
|
||||||
0.05: Updated clkinfo icon.
|
0.05: Updated clkinfo icon.
|
||||||
0.06: Ensure Timer supplies an image for clkinfo items
|
0.06: Ensure Timer supplies an image for clkinfo items
|
||||||
0.07: Update clock_info to avoid a redraw
|
0.07: Update clock_info to avoid a redraw
|
||||||
0.08: Timer ClockInfo now updates once a minute
|
0.08: Timer ClockInfo now updates once a minute
|
||||||
|
0.09: Timer ClockInfo resets to timer menu when blurred
|
||||||
|
|
|
||||||
|
|
@ -71,13 +71,19 @@
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const restoreMainItem = function(clkinfo) {
|
||||||
|
clkinfo.menuB = 0;
|
||||||
|
// clock info redraws after this
|
||||||
|
};
|
||||||
|
|
||||||
var offsets = [+5,-5];
|
var offsets = [+5,-5];
|
||||||
offsets.forEach((o, i) => {
|
offsets.forEach((o, i) => {
|
||||||
smpltmrItems.items = smpltmrItems.items.concat({
|
smpltmrItems.items = smpltmrItems.items.concat({
|
||||||
name: null,
|
name: null,
|
||||||
get: () => ({ text: (o > 0 ? "+" : "") + o + " min.", img: smpltmrItems.img }),
|
get: () => ({ text: (o > 0 ? "+" : "") + o + " min.", img: smpltmrItems.img }),
|
||||||
show: function() { },
|
show: function() { },
|
||||||
hide: function () { },
|
hide: function() { },
|
||||||
|
blur: restoreMainItem,
|
||||||
run: function() {
|
run: function() {
|
||||||
if(o > 0) increaseAlarm(o);
|
if(o > 0) increaseAlarm(o);
|
||||||
else decreaseAlarm(Math.abs(o));
|
else decreaseAlarm(Math.abs(o));
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "smpltmr",
|
"id": "smpltmr",
|
||||||
"name": "Simple Timer",
|
"name": "Simple Timer",
|
||||||
"shortName": "Simple Timer",
|
"shortName": "Simple Timer",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"description": "A very simple app to start a timer.",
|
"description": "A very simple app to start a timer.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,timer,clkinfo",
|
"tags": "tool,alarm,timer,clkinfo",
|
||||||
|
|
|
||||||
2
core
2
core
|
|
@ -1 +1 @@
|
||||||
Subproject commit bd301be3324775a8f464328ba9e34f750d503a2b
|
Subproject commit 0222d3c5ac608a1b842ffc1f1f79e19276d648fe
|
||||||
|
|
@ -11,10 +11,12 @@ declare module ClockInfo {
|
||||||
|
|
||||||
type MenuItem = {
|
type MenuItem = {
|
||||||
name: string,
|
name: string,
|
||||||
show(): void,
|
show(options: InteractiveOptions): void,
|
||||||
hide(): void,
|
hide(options: InteractiveOptions): void,
|
||||||
on(what: "redraw", cb: () => void): void, // extending from Object
|
on(what: "redraw", cb: () => void): void, // extending from Object
|
||||||
run?(): void,
|
run?(options: InteractiveOptions): void,
|
||||||
|
focus?(options: InteractiveOptions): void | false,
|
||||||
|
blur?(options: InteractiveOptions): void | false,
|
||||||
} & (
|
} & (
|
||||||
{
|
{
|
||||||
hasRange: true,
|
hasRange: true,
|
||||||
|
|
|
||||||
2
webtools
2
webtools
|
|
@ -1 +1 @@
|
||||||
Subproject commit af870d7b8386bfa824b07b268bce414e4daf3fbb
|
Subproject commit 8d671ad0dfb1d5a36f4ee9952390f4d79019e61d
|
||||||
Loading…
Reference in New Issue