Google Gauges for Home Assistant

Sorry, thought I had attached it in my previous post.

Config
panel_iframe:
guages:
title: Guages
url: http://192.168.200.210:8123/local/google-guages.html
icon: mdi:speedometer

google-guages.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Home Assistant Google Guages using WebSockets</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
    <meta name="description" content="A web interface for Home Assistant with WebSocket and Google Guages">
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js'></script>
    <script src='https://unpkg.com/[email protected]/dist/haws.umd.js'></script>
    <style>
        .flex-container {
          padding: 0;
          margin: 0;
          list-style: none;
          -ms-box-orient: horizontal;
          display: -webkit-box;
          display: -moz-box;
          display: -ms-flexbox;
          display: -moz-flex;
          display: -webkit-flex;
          display: flex;
        }

        .wrap    {
          -webkit-flex-wrap: wrap;
          flex-wrap: wrap;
        }

        .flex-item {
          padding: 0px;
          width: 150px;
          height: 150px;
          margin: 5px;

          line-height: 150px;
          color: white;
          font-weight: bold;
          font-size: 1em;
          text-align: center;
        }
    </style>

    <script type="text/javascript">

      function make_options(max) {
          return {
            width: 150, height: 150,
			greenFrom: 0.25*max, greenTo: 0.5*max,
            yellowFrom: 0.5*max, yellowTo: 0.75*max,
            redFrom: 0.75*max, redTo: max,
            minorTicks: 5,
            max: max
          };
      }
      //var URL = 'ws://' + location.host + '/api/websocket';
      var URL = 'ws://192.168.200.210:8123/api/websocket';
      var guages = {
        "sensor.disk_use_home": {name: "Disk GB",   div: "guage0_div", options: make_options(56)},
        "sensor.memory_use": {name: "Ram MiB",   div: "guage1_div", options: make_options(1000)},
		"sensor.memory_free": {name: "Ram MiB",   div: "guage2_div", options: make_options(1000)},
        "sensor.processor_use":      {name: "CPU %",   div: "guage3_div", options: make_options(100)},
        "sensor.cpu_speed":   {name: "CPU GHZ",      div: "guage6_div", options: make_options(12)},		
        "sensor.cpu_temperature":  {name: "CPU Ā°F",     div: "guage7_div", options: make_options(200)},
		"sensor.hass_db_size":  {name: "DB Size",     div: "guage8_div", options: make_options(64)},
		
        "sensor.living_room_thermostat_temperature":   {name: "Living",      div: "guage9_div", options: make_options(100)},
        "sensor.master_bedroom_temperature":   {name: "Master",      div: "guage10_div", options: make_options(100)},
        "sensor.girls_room_temperature":  {name: "Girls", div: "guage11_div", options: make_options(100)},
        "sensor.boys_room_temperature":   {name: "Boys",      div: "guage12_div", options: make_options(100)},
		"sensor.vision_zp3111_multisensor_4in1_temperature":   {name: "Comp",      div: "guage13_div", options: make_options(100)},
		
        "sensor.living_room_thermostat_humidity":   {name: "Living",    div: "guage14_div", options: make_options(100)},
		"sensor.master_bedroom_humidity":   {name: "Master",    div: "guage15_div", options: make_options(100)},
		"sensor.girls_room_humidity":   {name: "Girls",    div: "guage16_div", options: make_options(100)},
		"sensor.boys_room_humidity":   {name: "Boys",    div: "guage17_div", options: make_options(100)},
		"sensor.vision_zp3111_multisensor_4in1_relative_humidity":   {name: "Comp",    div: "guage18_div", options: make_options(100)},
		
		"sensor.master_bedroom_lux_level":   {name: "Master",    div: "guage19_div", options: make_options(100)},
		"sensor.girls_room_lux_level":   {name: "Girls",    div: "guage20_div", options: make_options(100)},
		"sensor.boys_room_lux_level":   {name: "Boys",    div: "guage21_div", options: make_options(100)},
		"sensor.vision_zp3111_multisensor_4in1_luminance":   {name: "Comp",    div: "guage22_div", options: make_options(100)},
		"sensor.dome_motion_detector_luminance":   {name: "Bath",    div: "guage23_div", options: make_options(100)},

		"sensor.bruce_fuel_level":   {name: "Bruce",    div: "guage24_div", options: make_options(100)},
		"sensor.chevy_fuel_level":   {name: "Chevy",    div: "guage25_div", options: make_options(100)}
		
      };

      function init() {
        const status = document.getElementById('status');
        google.charts.load('current', {'packages':['gauge']});
        google.charts.setOnLoadCallback(drawChart);

        function render(entities) {
          _.forEach(guages, function(guage, key) {
            try {
              var svalue = entities[key].state;
              guage["data"].setValue(0, 1, svalue);
              guage["chart"].draw(guage["data"], guage["options"]);
            } catch(e) {
              // Uncomment to see all state read failures in console
              // console.log("Error reading state for key: " + key);
              status.innerHTML = "Error reading state for key: " + key;
            }
          });
        }

        function drawChart() {
          _.forEach(guages, function(guage) {
            guage.data = google.visualization.arrayToDataTable([['Label', 'Value'], [guage.name, -1]]);
            guage.chart = new google.visualization.Gauge(document.getElementById(guage.div));
            guage.chart.draw(guage.data, guage.options);
          });
          HAWS.createConnection(URL).then(conn => {
            status.innerHTML = 'Connected';
            HAWS.subscribeEntities(conn, render);
          }, err => { status.innerHTML = `Connection error: (${err})`; });
       }

    }

    </script>

</head>

<body onload="init();">
<h4><span id='status'>Connecting...</span></h4>
<h1>System Resouces</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage0_div'></li>
<li class="flex-item" id='guage1_div'></li>
<li class="flex-item" id='guage2_div'></li>
<li class="flex-item" id='guage3_div'></li>
<li class="flex-item" id='guage6_div'></li>
<li class="flex-item" id='guage7_div'></li>
<li class="flex-item" id='guage8_div'></li>
</ul>
<h1>Room Temp Ā°F</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage9_div'></li>
<li class="flex-item" id='guage10_div'></li>
<li class="flex-item" id='guage11_div'></li>
<li class="flex-item" id='guage12_div'></li>
<li class="flex-item" id='guage13_div'></li>
</ul>
<h1>Humidity Level %</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage14_div'></li>
<li class="flex-item" id='guage15_div'></li>
<li class="flex-item" id='guage16_div'></li>
<li class="flex-item" id='guage17_div'></li>
<li class="flex-item" id='guage18_div'></li>
</ul>
<h1>LUX Level %</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage19_div'></li>
<li class="flex-item" id='guage20_div'></li>
<li class="flex-item" id='guage21_div'></li>
<li class="flex-item" id='guage22_div'></li>
<li class="flex-item" id='guage23_div'></li>
</ul>
<h1>Fuel Level %</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage24_div'></li>
<li class="flex-item" id='guage25_div'></li>
</ul>

</body>
</html>

Iā€™m seeing a connection error using your code with my IP address. Try to get one sensor working first and run it under Google Chrome Developer tools debugger.

One more thing. The error is an authentication error. I use the Legacy password which was created long ago. You must have the variable PASSWORD defined and contain your legacy password or authenticate with a valid username/password. I see that you are not connecting properly.

You should have:

    //add authentation with options parameter
    var options = { authToken: PASSWORD };
    HAWS.createConnection(URL, options).then(conn => {
      status.innerHTML = 'Connected';
      HAWS.subscribeConfig(conn, config => console.log('New config!', config));
      HAWS.subscribeEntities(conn, render);
    }, err => { status.innerHTML = "Connection error: " + GetErrorCode(err); });
  }

Do I need to add this to the html file?

Added the below section with the api password, but itā€™s still the same.

Was the following added after the initial release, as my html file didnā€™t include it?

  ///// Modify to suit your configeration /////
  var URL = 'ws://' + window.location.hostname + ':8123/api/websocket';
  var PASSWORD = 'XXXXXX'; //Only used if ther server requires authentication

Yes. Replace that section in your code.

I tried with the following, but it just shows connecting?

The page loads correctly but Iā€™m getting ERR_INVALID_AUTH notice and the gauge (I only set up one for now) does not show a reading.

Probably a silly question, but the PASSWORD variable is my Home Assistant password correct? Iā€™ve cleared my cache. Iā€™ve logged out and back into Home Assistant with a username/password instead of Trusted Network.

No errors show up in the Chrome Developer Console. I get the following error in my Home Assistant log.

Login attempt or request with invalid authentication from 192.168.1.141

This is my config. Had to change url to wss because Iā€™m running HTTPS. Should it work configured like this?

  var URL = 'wss://' + window.location.hostname + ':8123/api/websocket';
  var PASSWORD = 'MYHASSIOPASSWORD'; //Only used if ther server requires authentication

Any idea where Iā€™ve gone wrong?

I use my HA legacy password. My code works and so does Coolieā€™s - except I donā€™t have his devices.

The entirety of the DrawChart function should be as follows::

    function drawChart() {
      _.forEach(guages, function(guage) {
        guage.data = google.visualization.arrayToDataTable([['Label', 'Value'], [guage.name, -1]]);
        guage.chart = new google.visualization.Gauge(document.getElementById(guage.div));
        guage.chart.draw(guage.data, guage.options);
      });
    //add authentation with options parameter
    var options = { authToken: PASSWORD };
    HAWS.createConnection(URL, options).then(conn => {
      status.innerHTML = 'Connected';
      HAWS.subscribeConfig(conn, config => console.log('New config!', config));
      HAWS.subscribeEntities(conn, render);
    }, err => { status.innerHTML = "Connection error: " + GetErrorCode(err); });
  }
1 Like

I didnā€™t have legacy_api_password defined under auth_providers. Working now. Thank you.

@JerryWorkman
Sorry man, still struggling to get this back up and running, now I get no gauges with the config below, any ideas?

google-guages.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Home Assistant Google Guages using WebSockets</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
    <meta name="description" content="A web interface for Home Assistant with WebSocket and Google Guages">
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js'></script>
    <script src='https://unpkg.com/[email protected]/dist/haws.umd.js'></script>
    <style>
        .flex-container {
          padding: 0;
          margin: 0;
          list-style: none;
          -ms-box-orient: horizontal;
          display: -webkit-box;
          display: -moz-box;
          display: -ms-flexbox;
          display: -moz-flex;
          display: -webkit-flex;
          display: flex;
        }

        .wrap    {
          -webkit-flex-wrap: wrap;
          flex-wrap: wrap;
        }

        .flex-item {
          padding: 0px;
          width: 150px;
          height: 150px;
          margin: 5px;

          line-height: 150px;
          color: white;
          font-weight: bold;
          font-size: 2em;
          text-align: center;
        }
    </style>

    <script type="text/javascript">

      ///// Modify to suit your configeration /////
      var URL = 'ws://192.168.200.210:8123/api/websocket';
      var PASSWORD = 'MY_HA_API_PASSWORD'; //Only used if ther server requires authentication

      function make_options(max) {
          return {
            width: 150, height: 150,
            yellowFrom: 0.5*max, yellowTo: 0.75*max,
            redFrom: 0.75*max, redTo: max,
            minorTicks: 5,
            max: max
          };
      }
      var guages = {
        "sensor.disk_use_home": {name: "Disk GB",   div: "guage0_div", options: make_options(56)},
        "sensor.memory_use": {name: "Ram MiB",   div: "guage1_div", options: make_options(1000)},
		"sensor.memory_free": {name: "Ram MiB",   div: "guage2_div", options: make_options(1000)},
        "sensor.processor_use":      {name: "CPU %",   div: "guage3_div", options: make_options(100)},
        "sensor.cpu_speed":   {name: "CPU GHZ",      div: "guage6_div", options: make_options(12)},		
        "sensor.cpu_temperature":  {name: "CPU Ā°F",     div: "guage7_div", options: make_options(200)},
		"sensor.hass_db_size":  {name: "DB Size",     div: "guage8_div", options: make_options(64)},
		
        "sensor.living_room_thermostat_temperature":   {name: "Living",      div: "guage9_div", options: make_options(100)},
        "sensor.master_bedroom_temperature":   {name: "Master",      div: "guage10_div", options: make_options(100)},
        "sensor.girls_room_temperature":  {name: "Girls", div: "guage11_div", options: make_options(100)},
        "sensor.boys_room_temperature":   {name: "Boys",      div: "guage12_div", options: make_options(100)},
		"sensor.vision_zp3111_multisensor_4in1_temperature":   {name: "Comp",      div: "guage13_div", options: make_options(100)},
		
        "sensor.living_room_thermostat_humidity":   {name: "Living",    div: "guage14_div", options: make_options(100)},
		"sensor.master_bedroom_humidity":   {name: "Master",    div: "guage15_div", options: make_options(100)},
		"sensor.girls_room_humidity":   {name: "Girls",    div: "guage16_div", options: make_options(100)},
		"sensor.boys_room_humidity":   {name: "Boys",    div: "guage17_div", options: make_options(100)},
		"sensor.vision_zp3111_multisensor_4in1_relative_humidity":   {name: "Comp",    div: "guage18_div", options: make_options(100)},
		
		"sensor.master_bedroom_lux_level":   {name: "Master",    div: "guage19_div", options: make_options(100)},
		"sensor.girls_room_lux_level":   {name: "Girls",    div: "guage20_div", options: make_options(100)},
		"sensor.boys_room_lux_level":   {name: "Boys",    div: "guage21_div", options: make_options(100)},
		"sensor.vision_zp3111_multisensor_4in1_luminance":   {name: "Comp",    div: "guage22_div", options: make_options(100)},
		"sensor.dome_motion_detector_luminance":   {name: "Bath",    div: "guage23_div", options: make_options(100)},

		"sensor.bruce_fuel_level":   {name: "Bruce",    div: "guage24_div", options: make_options(100)},
		"sensor.chevy_fuel_level":   {name: "Chevy",    div: "guage25_div", options: make_options(100)}
		
      };

      function init() {
        const status = document.getElementById('status');
        google.charts.load('current', {'packages':['gauge']});
        google.charts.setOnLoadCallback(drawChart);

        function render(entities) {
          //HAWS.getGroupEntities(entities, "group.Power" => console.log('Power Entities!', entities));
          //HAWS.splitByGroups(entities => console.log('Group Entities!', entities));
          var error = false;
          _.forEach(guages, function(guage, key) {
            try {
              var svalue = entities[key].state;
              if( svalue < 0 ) { svalue = 0; }
              guage["data"].setValue(0, 1, svalue);
              //guage["chart"].draw(guage["data"], guage["options"]);
              guage["chart"].draw(guage["data"], make_options(guage["max"]));
            } catch(e) {
              // Uncomment to see all state read failures in console
              // console.log("Error reading state for key: " + key);
              status.innerHTML = "Error reading state for key: " + key;
              error = true;
            }
            if(!error) status.innerHTML = "Updated: " + (new Date()).toLocaleString();
          });

        }

        function getConfig(config) {
          console.log(config);
        }

        function GetErrorCode(err) {
          if(err == 1) return 'ERR_CANNOT_CONNECT';
          if(err == 2) return 'ERR_INVALID_AUTH';
          return 'UNKNOWN_ERROR';
        }

        function drawChart() {
          _.forEach(guages, function(guage) {
            guage.data = google.visualization.arrayToDataTable([['Label', 'Value'], [guage.label, -1]]);
            guage.chart = new google.visualization.Gauge(document.getElementById(guage.div));
            guage.chart.draw(guage.data, guage.options);
          });
          //add authentation with options parameter
          var options = {authToken: PASSWORD};
          HAWS.createConnection(URL, options).then(conn => {
            status.innerHTML = 'Connected';
            HAWS.subscribeConfig(conn, config => console.log('New config!', config));
            HAWS.subscribeEntities(conn, render);
          }, err => { status.innerHTML = "Connection error: " + GetErrorCode(err); });
       }

    }

    </script>

</head>

<body onload="init();">
<h4><span id='status'>Connecting...</span></h4>
<h1>System Resouces</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage0_div'></li>
<li class="flex-item" id='guage1_div'></li>
<li class="flex-item" id='guage2_div'></li>
<li class="flex-item" id='guage3_div'></li>
<li class="flex-item" id='guage6_div'></li>
<li class="flex-item" id='guage7_div'></li>
<li class="flex-item" id='guage8_div'></li>
</ul>
<h1>Room Temp Ā°F</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage9_div'></li>
<li class="flex-item" id='guage10_div'></li>
<li class="flex-item" id='guage11_div'></li>
<li class="flex-item" id='guage12_div'></li>
<li class="flex-item" id='guage13_div'></li>
</ul>
<h1>Humidity Level %</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage14_div'></li>
<li class="flex-item" id='guage15_div'></li>
<li class="flex-item" id='guage16_div'></li>
<li class="flex-item" id='guage17_div'></li>
<li class="flex-item" id='guage18_div'></li>
</ul>
<h1>LUX Level %</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage19_div'></li>
<li class="flex-item" id='guage20_div'></li>
<li class="flex-item" id='guage21_div'></li>
<li class="flex-item" id='guage22_div'></li>
<li class="flex-item" id='guage23_div'></li>
</ul>
<h1>Fuel Level %</h1>
<ul class="flex-container wrap">
<li class="flex-item" id='guage24_div'></li>
<li class="flex-item" id='guage25_div'></li>
</ul>

</body>
</html>

The error message tells the tale. Authentication fails. You are using the wrong password.

A more suitable auth scheme for scripts is described here:

Just create your token, open //YOURIP:8123/profile then at the bottom of the page create a Long Lived Access Token. Save it to a safe place, and change:

var options = { authToken: PASSWORD };
to:
var options { Authorization: ā€˜Bearer PASTE-YOUR-AUTH-TOKEN-HEREā€™ }

Worked first time for me.