Skip to content

Instantly share code, notes, and snippets.

@JerryWorkman
Last active June 10, 2022 17:22
Show Gist options
  • Save JerryWorkman/d64740b619b211f6398eb67f39e2e438 to your computer and use it in GitHub Desktop.
Save JerryWorkman/d64740b619b211f6398eb67f39e2e438 to your computer and use it in GitHub Desktop.
Home Assistant Dashboard using WebSocket API

ha-websockets

Home Assistant Dashboard using WebSocket API

An easy to configure kiosk type display of HomeAssistant switches, lights, and sensors. Switches and lights can be turned off and on buy clicking on the icon. The icon color will change to green when on or red when off.

Installation

Copy ha-websocket.html to the .homeassistant/www/ directory. Edit the file and modify the ip address / host name as well as groups

        var URL = 'ws://' + window.location.hostname + ':8123/api/websocket';
        var PASSWORD = 'XXXXXX'; //Your HA password
        var groups = ["group.all_switches","group.all_lights","group.battery_levels"];

Everyone should have "group.all_switches" and "group.all_lights" as HA creates those automatically.

If you wish to build home-assistant-js-websocket from source see: building-home-assistant-js-websocket.md

Use:

Open http://host:8123/local/ha-websocket.html

Or add the following to your configuration.yaml

panel_iframe:
  ha_websocket:
    title: Websocket Panel
    icon: mdi:hand-pointing-right
    url: http://host:8123/local/ha-websocket.html

What's next

  • Fancy buttons
  • Automatic button resizing for small screens
  • Additional widgets for complex sensors / switches
  • Camera Images
  • remove lodash.js dependency

Building home-assistant-js-websocket

Easy way:

Use this CDN (from the author @balloob)

<script src='https://cdn.jsdelivr.net/npm/home-assistant-js-websocket@1.1.2/dist/haws.umd.min.js'></script>

Do not use: 'https://unpkg.com/home-assistant-js-websocket@0.4.1/dist/haws.umd.js'; getGroupEntities(entities, group) and splitByGroups(entities) are missing! Must be an old version

Or build the current version from GitHub

(Linux)

Install nodejs and npm

sudo apt-get update
sudo apt-get install nodejs
sudo apt-get install npm

cd to your config directory - create www directory if necessary

cd .homeassistant/www

grab websocket source

git clone https://github.com/home-assistant/home-assistant-js-websocket.git
cd home-assistant-js-websocket

Note that the file rollup.config.js is present so we use rollup to package the javascript.

install rollup - see https://code.lengstorf.com/learn-rollup-js/

npm install --save-dev rollup

compile to dist directory

./node_modules/.bin/rollup -c
ls dist

You should see the files haws.es.js haws.umd.js. Not (yet) uglified; readable - debuggable code

Now in your html add:

<script src='home-assistant-js-websocket/dist/haws.umd.js'></script>

You can then:

HAWS.createConnection(URL).then(conn => {
  wsconn = conn;
  status.innerHTML = 'Connected';
  HAWS.subscribeEntities(conn, render);
  HAWS.subscribeConfig(conn, config => LoadConfig(config));
}, err => { status.innerHTML = `Connection error: (${err})`; });

as well as

getGroupEntities(entities, group);

and

splitByGroups(entities);

in your render function

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Home Assistant Dashboard using WebSocket API">
<title>Home Assistant using WebSocket</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css" rel="stylesheet">
<style>
.button {
background-color: #03A9F4;
border: none;
color: white;
padding: 24px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
.button-on {
background-color: green;
}
.button-off {
background-color: red;
}
</style>
<script src='http://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/home-assistant-js-websocket@1.1.2/dist/haws.umd.min.js'></script>
<script>
///// Modify to suit your configeration /////
var URL = 'ws://' + window.location.hostname + ':8123/api/websocket';
var PASSWORD = 'XXXXXX'; // Your HA password if any
var groups = ["group.all_switches","group.all_lights","group.living_room","group.power","group.battery_levels"];
/////////////////////////////////////////////
var wsconn;
String.prototype.format = function () {
var args = arguments;
return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; });
};
String.prototype.capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
}
CallCommand = function(domain, service, obj) {
var service_data = {
entity_id: domain + "." + obj.id
};
wsconn.callService(domain, service, service_data);
};
UpdateSwitchButton = function(key, state) {
id = '#' + key;
if(state == "on") {
$( id ).removeClass( "button-off" ).addClass( "button-on" );
} else {
$( id ).removeClass( "button-on" ).addClass( "button-off" );
}
};
UpdateSensorButton = function(key, state) {
//$( '#lr_pir_battery > span').text(" foo");
var id = '#' + key + '>span';
if(state < 0) state = "0";
$( id ).html( "<br>" + state );
};
var states = {};
var buttons_created = false;
$(document).ready(function() {
const status = document.getElementById('status');
var CreateAllButtons = function(groups, entities) {
for (var i = 0, len = groups.length; i < len; i++) {
var group_name = groups[i];
// TODO: handle group within group
// TODO: get subgroups and CreateButtons
var gentities = HAWS.getGroupEntities(entities, entities[group_name]);
CreateButtons(group_name, gentities);
}
}
var CreateButtons = function(group_name, entities) {
console.log("CreateButtons", group_name);
var name = group_name.replace("_", " ").split(".")[1].capitalize();
$( 'div#buttons' ).append( "<h3>" + name + "</h3>" );
var btn, button;
_.forEach(entities, function(entity, entity_id) {
var domain = entity_id.split(".")[0];
var id = entity_id.split(".")[1];
var name = entity.attributes.friendly_name
switch(domain) {
case "switch":
button =
"<button id = \"{0}\" class=\"button switch\"><i class=\"fa fa-lightbulb-o\"></i>&nbsp;{1}</button>";
break;
case "light":
button =
"<button id = \"{0}\" class=\"button light\"><i class=\"fa fa-lightbulb-o\"></i>&nbsp;{1}</button>";
break;
case "sensor":
case "binary_sensor":
default:
button =
"<button id = \"{0}\" class=\"button sensor\"><i class=\"fa thermometer-full\"></i>&nbsp;{1}<span class=\"value\">?</span></button>";
};
var btn = button.format(id, name);
$( 'div#buttons' ).append( btn );
});
$('button.switch').click(function() {
CallCommand("switch", "toggle", this);
});
$('button.light').click(function() {
CallCommand("light", "toggle", this);
});
};
function render(entities) {
if( ! buttons_created) {
CreateAllButtons(groups, entities);
buttons_created = true;
}
for (var i = 0, len = groups.length; i < len; i++) {
var group_name = groups[i];
var gentities = HAWS.getGroupEntities(entities, entities[group_name]);
_.forEach(gentities, function(entity, id) {
domain = id.split(".")[0];
key = id.split(".")[1];
switch(domain) {
case "switch":
case "light":
UpdateSwitchButton(key, entity.state);
break;
default:
UpdateSensorButton(key, entity.state);
}
});
}
}
function GetErrorCode(err) {
if(err == 1) return 'ERR_CANNOT_CONNECT';
if(err == 2) return 'ERR_INVALID_AUTH';
return 'UNKNOWN_ERROR';
}
//add authentation with options parameter
var options = {authToken: PASSWORD};
HAWS.createConnection(URL, options).then(conn => {
wsconn = conn;
status.innerHTML = 'Connected';
HAWS.subscribeEntities(conn, render);
}, err => { status.innerHTML = "Connection error: " + GetErrorCode(err); });
});
</script>
</head>
<body>
<div class="container">
<div class="page-header">
<h1>Home Assistant Dashboard using WebSocket API</h1>
</div>
<div class="container-fluid" style="margin-top: 20px;">
<div id="status"></div>
<div class="panel panel-default">
<div class="panel-body">
<div id="buttons"></div>
</div>
<div class="panel-body">
<div id="samplebuttons" style="display:none">
<button class="button"><i class="fa fa-reply"></i></button>
<button class="button button-on"><i class="fa fa-star"></i></button>
<button class="button button-off"><i class="fa fa-trash-o"></i></button>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
@JerryWorkman
Copy link
Author

Add authentication and error messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment