Merge pull request #2000 from peerdavid/master

Refactoring and improvements of HomeAssistant, BWClock and Info App.
pull/2003/head
Gordon Williams 2022-06-28 08:21:01 +01:00 committed by GitHub
commit 0fadb39ae9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 353 additions and 190 deletions

View File

@ -6,4 +6,5 @@
0.06: Design and usability improvements.
0.07: Improved positioning.
0.08: Select the color of widgets correctly. Additional settings to hide colon.
0.09: Larger font size if colon is hidden to improve readability further.
0.09: Larger font size if colon is hidden to improve readability further.
0.10: HomeAssistant integration if HomeAssistant is installed.

View File

@ -7,6 +7,7 @@
- Tab left/right of screen to show steps, temperature etc.
- Enable / disable lock icon in the settings.
- If the "sched" app is installed tab top / bottom of the screen to set the timer.
- If HomeAssistant is installed, triggers are shown. Simple select the trigger and touch the middle of the screen to send the trigger to HomeAssistant.
- The design is adapted to the theme of your bangle.
- The colon (e.g. 7:35 = 735) can be hidden now in the settings.

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,11 @@
{
"id": "bwclk",
"name": "BW Clock",
"version": "0.09",
"version": "0.10",
"description": "BW Clock.",
"readme": "README.md",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}],
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}, {"url":"screenshot_4.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],

BIN
apps/bwclk/screenshot_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1 +1,2 @@
0.01: Release
0.01: Release
0.02: Includeas the ha.lib.js library that can be used by other apps or clocks.

View File

@ -1,13 +1,15 @@
# Home Assistant
This app integrates your BangleJs into the HomeAssistant.
# How to use
Click on the left and right side of the screen to select the triggers that you
configured. Click in the middle of the screen to send the trigger to HomeAssistant.
![](screenshot.png)
# First Setup
# Initial Setup
1.) First of all, make sure that HomeAssistant and the HomeAssistant Android App works.
2.) Open your BangleJs Gadgetbridge App, click on the Settings icon of your BangleJs and enable "Allow Intent Access"
@ -22,6 +24,7 @@ configured. Click in the middle of the screen to send the trigger to HomeAssista
This setup must be done only once -- now you are ready to configure your BangleJS to
control some devices or entities in your HomeAssistant :)
# Setup Trigger
1.) Upload the app and all corresponding triggers through the AppStore UI. You must specify
the display name, the trigger as well as an icon.
@ -38,12 +41,36 @@ The following icons are currently supported:
3.) Don't forget to select the action that should be executed at the bottom of each automation.
# Default Trigger
This app also implements two default trigger that can always be used:
- APP_STARTED -- Will be sent whenever the app is started. So you could do some actions already when the app is sarted without the need of any user interaction.
- TRIGGER -- Will be sent whenever some trigger is executed. So you could generically listen to that.
# How to use the library (ha.lib.js) in my own app/clk
This app inlcludes a library that can be used by other apps or clocks
to read all configured intents or to send a trigger. Example code:
```js
// First of all impport the library
var ha = require("ha.lib.js");
// You can read all triggers that a user configured simply via
var triggers = ha.getTriggers();
// Get display name and icon of trigger
var display = triggers[0].display;
var icon = triggers[0].getIcon();
// Trigger the first configured trigger
ha.sendTrigger(triggers[0].trigger);
// Send a custom trigger that is not configured by a user
ha.sendTrigger("MY_CUSTOM_TRIGGER");
```
# FAQ
## Sometimes the trigger is not executed

View File

@ -1,72 +1,10 @@
var storage = require("Storage");
/**
* This app uses the ha library to send trigger to HomeAssistant.
*/
var ha = require("ha.lib.js");
var W = g.getWidth(), H = g.getHeight();
var position=0;
// Note: All icons should have 48x48 pixels
function getIcon(icon){
if(icon == "light"){
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AAMBwAFE4AFDgYFJjgFBnAFBjwXBvAFBh4jBuAFCAQPwAQMHAQPgEQQCBEgcf/AvDn/8Aof//5GDAoJOBh+BAoOB+EP8YFB4fwgfnAoPnGANHAoPjHYQFBHYQFd44pDg47C4/gh/DIIZNFLIplGgF//wFIgZ9BRIUHRII7Ch4FBUIUOAoKzCjwFEhgCBmDpIVooFFh4oCAA4LFC5b7BAob1BAYI="))
};
} else if(icon == "door"){
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AAM4Aok/4AED///Aov4Aon8DgQGBAv4FpnIFKJv4FweAQFFAgQFB8AFDnADC"))
};
} else if (icon == "fire"){
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("ABsDAokBwAFE4AFE8AFE+AFE/AFJgf8Aon+AocHAokP/8QAokYAoUfAok//88ApF//4kDAo//AgMQAgIFCjgFEjwFCOYIFFHQIFDn/+AoJ/BAoIqBAoN//xCBAoI5BDIPAgP//gFB8AFChYFBgf//EJAogOBAoSgBAoMHAQIFEFgXAAoJEBv4FCNoQFGVYd/wAFEYYIFIvwCBDoV8UwQCBcgUPwDwDfQMBaIYADA"))
};
}
// Default is always the HA icon
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AD8BwAFDg/gAocP+AFDj4FEn/8Aod//wFD/1+FAf4j+8AoMD+EPDAUH+OPAoUP+fPAoUfBYk/C4l/EYIwC//8n//FwIFEgYFD4EH+E8nkP8BdBAonjjk44/wj/nzk58/4gAFDF4PgCIMHAoPwhkwh4FB/EEkEfIIWAHwIFC4A+BAoXgg4FDL4IFDL4IFDLIYFkAEQA=="))
};
}
// Try to read custom actions, otherwise use default
var triggers = [
{display: "Not found.", trigger: "NOP", icon: "ha"},
];
try{
triggers = storage.read("ha.trigger.json");
triggers = JSON.parse(triggers);
} catch(e) {
// In case there are no user triggers yet, we show the default...
}
function sendIntent(trigger){
var retries=3;
while(retries > 0){
try{
// Send a startup trigger such that we could also execute
// an action when the app is started :)
Bluetooth.println(JSON.stringify({
t:"intent",
action:"com.espruino.gadgetbridge.banglejs.HA",
extra:{
trigger: trigger
}})
);
retries = -1;
} catch(e){
retries--;
}
}
}
var triggers = ha.getTriggers();
function draw() {
@ -78,7 +16,7 @@ function draw() {
var w = g.stringWidth(trigger.display);
g.setFontAlign(-1,-1);
var icon = getIcon(trigger.icon);
var icon = trigger.getIcon();
g.setColor(g.theme.fg).drawImage(icon, 12, H/5-2);
g.drawString("Home", icon.width + 20, H/5);
g.drawString("Assistant", icon.width + 18, H/5+24);
@ -112,13 +50,11 @@ Bangle.on('touch', function(btn, e){
}
if(!isRight && !isLeft){
// Send a default intent that we triggered something.
sendIntent("TRIGGER");
ha.sendTrigger("TRIGGER");
// Now send the selected trigger
Bangle.buzz(80, 0.6).then(()=>{
sendIntent(triggers[position].trigger);
ha.sendTrigger(triggers[position].trigger);
setTimeout(()=>{
Bangle.buzz(80, 0.6);
}, 250);
@ -126,12 +62,14 @@ Bangle.on('touch', function(btn, e){
}
});
// Send intent that the we started the app.
sendIntent("APP_STARTED");
ha.sendTrigger("APP_STARTED");
// Next load the widgets and draw the app
Bangle.loadWidgets();
Bangle.drawWidgets();
// Draw app
draw();
setWatch(_=>load(), BTN1);

80
apps/ha/ha.lib.js Normal file
View File

@ -0,0 +1,80 @@
/**
* This library can be used to read all triggers that a user
* configured and send a trigger to homeassistant.
*/
function _getIcon(trigger){
icon = trigger.icon;
if(icon == "light"){
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AAMBwAFE4AFDgYFJjgFBnAFBjwXBvAFBh4jBuAFCAQPwAQMHAQPgEQQCBEgcf/AvDn/8Aof//5GDAoJOBh+BAoOB+EP8YFB4fwgfnAoPnGANHAoPjHYQFBHYQFd44pDg47C4/gh/DIIZNFLIplGgF//wFIgZ9BRIUHRII7Ch4FBUIUOAoKzCjwFEhgCBmDpIVooFFh4oCAA4LFC5b7BAob1BAYI="))
};
} else if(icon == "door"){
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AAM4Aok/4AED///Aov4Aon8DgQGBAv4FpnIFKJv4FweAQFFAgQFB8AFDnADC"))
};
} else if (icon == "fire"){
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("ABsDAokBwAFE4AFE8AFE+AFE/AFJgf8Aon+AocHAokP/8QAokYAoUfAok//88ApF//4kDAo//AgMQAgIFCjgFEjwFCOYIFFHQIFDn/+AoJ/BAoIqBAoN//xCBAoI5BDIPAgP//gFB8AFChYFBgf//EJAogOBAoSgBAoMHAQIFEFgXAAoJEBv4FCNoQFGVYd/wAFEYYIFIvwCBDoV8UwQCBcgUPwDwDfQMBaIYADA"))
};
}
// Default is always the HA icon
return {
width : 48, height : 48, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AD8BwAFDg/gAocP+AFDj4FEn/8Aod//wFD/1+FAf4j+8AoMD+EPDAUH+OPAoUP+fPAoUfBYk/C4l/EYIwC//8n//FwIFEgYFD4EH+E8nkP8BdBAonjjk44/wj/nzk58/4gAFDF4PgCIMHAoPwhkwh4FB/EEkEfIIWAHwIFC4A+BAoXgg4FDL4IFDL4IFDLIYFkAEQA=="))
};
}
exports.getTriggers = function(){
var triggers = [
{display: "Empty", trigger: "NOP", icon: "ha"},
];
try{
triggers = require("Storage").read("ha.trigger.json");
triggers = JSON.parse(triggers);
// We lazy load all icons, otherwise, we have to keep
// all the icons n times in memory which can be
// problematic for embedded devices. Therefore,
// we lazy load icons only if needed using the getIcon
// method of each trigger...
triggers.forEach(trigger => {
trigger.getIcon = function(){
return _getIcon(trigger);
}
})
} catch(e) {
// In case there are no user triggers yet, we show the default...
}
return triggers;
}
exports.sendTrigger = function(triggerName){
var retries=3;
while(retries > 0){
try{
// Now lets send the trigger that we sould send.
Bluetooth.println(JSON.stringify({
t:"intent",
action:"com.espruino.gadgetbridge.banglejs.HA",
extra:{
trigger: triggerName
}})
);
retries = -1;
} catch(e){
retries--;
}
}
}

View File

@ -1,7 +1,7 @@
{
"id": "ha",
"name": "HomeAssistant",
"version": "0.01",
"version": "0.02",
"description": "Integrates your BangleJS into HomeAssistant.",
"icon": "ha.png",
"type": "app",
@ -19,6 +19,7 @@
],
"storage": [
{"name":"ha.app.js","url":"ha.app.js"},
{"name":"ha.lib.js","url":"ha.lib.js"},
{"name":"ha.img","url":"ha.icon.js","evaluate":true}
]
}

View File

@ -1 +1,2 @@
0.01: Release
0.01: Release
0.02: Recfactoring and show weather data.

View File

@ -1,27 +1,90 @@
var s = require("Storage");
const storage = require("Storage");
const locale = require('locale');
var ENV = process.env;
var W = g.getWidth(), H = g.getHeight();
var screen = 0;
const maxScreen = 2;
var screens = [
{
name: "General",
items: [
{name: "Steps", fun: () => getSteps()},
{name: "HRM", fun: () => getBpm()},
{name: "", fun: () => ""},
{name: "Temp.", fun: () => getWeatherTemp()},
{name: "Humidity", fun: () => getWeatherHumidity()},
{name: "Wind", fun: () => getWeatherWind()},
]
},
{
name: "Hardware",
items: [
{name: "Battery", fun: () => E.getBattery() + "%"},
{name: "Charge?", fun: () => Bangle.isCharging() ? "Yes" : "No"},
{name: "TempInt.", fun: () => locale.temp(parseInt(E.getTemperature()))},
{name: "Bluetooth", fun: () => NRF.getSecurityStatus().connected ? "Conn" : "NoConn"},
{name: "GPS", fun: () => Bangle.isGPSOn() ? "On" : "Off"},
{name: "Compass", fun: () => Bangle.isCompassOn() ? "On" : "Off"},
]
},
{
name: "Software",
items: [
{name: "Firmw.", fun: () => ENV.VERSION},
{name: "Boot.", fun: () => getVersion("boot.info")},
{name: "Settings.", fun: () => getVersion("setting.info")},
{name: "Storage.", fun: () => ""},
{name: " Total", fun: () => ENV.STORAGE>>10},
{name: " Free", fun: () => require("Storage").getFree()>>10},
]
}
];
function getWeatherTemp(){
try {
var weather = storage.readJSON('weather.json').weather;
return locale.temp(weather.temp-273.15);
} catch(ex) { }
return "?";
}
function getWeatherHumidity(){
try {
var weather = storage.readJSON('weather.json').weather;
return weather.hum = weather.hum + "%";
} catch(ex) { }
return "?";
}
function getWeatherWind(){
try {
var weather = storage.readJSON('weather.json').weather;
var speed = locale.speed(weather.wind).replace("mph", "");
return Math.round(speed * 1.609344) + "kph";
} catch(ex) { }
return "?";
}
function getVersion(file) {
var j = s.readJSON(file,1);
var j = storage.readJSON(file,1);
var v = ("object"==typeof j)?j.version:false;
return v?((v?"v"+v:"Unknown")):"NO ";
}
function drawData(name, value, y){
g.drawString(name, 5, y);
g.drawString(value, 100, y);
}
function getSteps(){
try{
return Bangle.getHealthStatus("day").steps;
} catch(e) {
return ">= 2v12";
return ">2v12";
}
}
@ -29,53 +92,36 @@ function getBpm(){
try{
return Math.round(Bangle.getHealthStatus("day").bpm) + "bpm";
} catch(e) {
return ">= 2v12";
return ">2v12";
}
}
function drawData(name, value, y){
g.drawString(name, 10, y);
g.drawString(value, 100, y);
}
function drawInfo() {
g.reset().clearRect(Bangle.appRect);
var h=18, y = h;//-h;
// Header
g.setFont("Vector", h+2).setFontAlign(0,-1);
g.drawString("--==|| INFO ||==--", W/2, 0);
g.drawLine(0,25,W,25);
g.drawLine(0,26,W,26);
// Info body depending on screen
g.setFont("Vector",h).setFontAlign(-1,-1);
screens[screen].items.forEach(function (item, index){
drawData(item.name, item.fun(), y+=h);
});
// Dynamic data
if(screen == 0){
drawData("Steps", getSteps(), y+=h);
drawData("HRM", getBpm(), y+=h);
drawData("Battery", E.getBattery() + "%", y+=h);
drawData("Voltage", E.getAnalogVRef().toFixed(2) + "V", y+=h);
drawData("IntTemp.", locale.temp(parseInt(E.getTemperature())), y+=h);
}
if(screen == 1){
drawData("Charging?", Bangle.isCharging() ? "Yes" : "No", y+=h);
drawData("Bluetooth", NRF.getSecurityStatus().connected ? "Conn." : "Disconn.", y+=h);
drawData("GPS", Bangle.isGPSOn() ? "On" : "Off", y+=h);
drawData("Compass", Bangle.isCompassOn() ? "On" : "Off", y+=h);
drawData("HRM", Bangle.isHRMOn() ? "On" : "Off", y+=h);
}
// Static data
if(screen == 2){
drawData("Firmw.", ENV.VERSION, y+=h);
drawData("Boot.", getVersion("boot.info"), y+=h);
drawData("Settings", getVersion("setting.info"), y+=h);
drawData("Storage", "", y+=h);
drawData(" Total", ENV.STORAGE>>10, y+=h);
drawData(" Free", require("Storage").getFree()>>10, y+=h);
}
if(Bangle.isLocked()){
g.setFont("Vector",h-2).setFontAlign(-1,-1);
g.drawString("Locked", 0, H-h+2);
}
// Bottom
g.drawLine(0,H-h-3,W,H-h-3);
g.drawLine(0,H-h-2,W,H-h-2);
g.setFont("Vector",h-2).setFontAlign(-1,-1);
g.drawString(screens[screen].name, 2, H-h+2);
g.setFont("Vector",h-2).setFontAlign(1,-1);
g.drawString((screen+1) + "/3", W, H-h+2);
g.drawString((screen+1) + "/" + screens.length, W, H-h+2);
}
drawInfo();
@ -88,14 +134,15 @@ Bangle.on('touch', function(btn, e){
var isRight = e.x > right;
if(isRight){
screen = (screen + 1) % (maxScreen+1);
screen = (screen + 1) % screens.length;
}
if(isLeft){
screen -= 1;
screen = screen < 0 ? maxScreen : screen;
screen = screen < 0 ? screens.length-1 : screen;
}
Bangle.buzz(40, 0.6);
drawInfo();
});
@ -104,5 +151,4 @@ Bangle.on('lock', function(isLocked) {
});
Bangle.loadWidgets();
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
// Bangle.drawWidgets();
Bangle.drawWidgets();

View File

@ -1,7 +1,7 @@
{
"id": "info",
"name": "Info",
"version": "0.01",
"version": "0.02",
"description": "An application that displays information such as battery level, steps etc.",
"icon": "info.png",
"type": "app",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB