Custom HTML

The Custom HTML Block is an advanced block that allows you to create data visualization using your own custom HTML, CSS, and JavaScript. With this block, you have full control over the representation of your data, and create almost any kind of visualization.

Custom HTML Examples

Configuring the Custom HTML Block is broken up into two sections:

Queries

Queries

All query types include a Query Name property which is how each individual query’s data is referenced in the data given to your block’s JavaScript code.

Time Series Queries

Time Series Queries provide a dataset that includes the value of a single attribute from a selected device over a duration of time at a selected resolution. The values returned from the query are returned as an array of objects in the form of:

[
  { "time": <time>, "value": <value> },
  { "time": <time>, "value": <value> },
  { "time": <time>, "value": <value> }
]

The values returned will reflect the selected attribute’s value specified at the reported time. This is the same data that is used in the Time Series Block.

Time Series Query

The parameters of the Time Series Query include:

  • Query Name is what will be referenced in your block’s JavaScript code.
  • Device IDS / Tags is a device query for choosing which devices’ data you want to access.
  • Attribute is the device attribute whose value is returned in the query. Note that if data from more than one device is being displayed, each of those devices must supply the same attribute name.
  • Duration is how far into the past you want to look at the data.
  • Resolution is how your data is grouped. These options change based on what you specify for the duration.
  • Aggregation determines how all the available data in each resolution group should be aggregated before being read. For example, “Mean” averages all data points together before displaying within the gauge.

Gauge Queries

Gauge Queries allow you to query a single attribute from a selected device. You can choose to return either the last reported value of the attribute or the value from a selected aggregation over a selected duration of time.

This query returns an object in the form of { "time": <time>, "value": <value> }. The value of time in the returned data is the time that the query was made and the value of value is the data of the attribute after the selected aggregation has been performed.

Gauge Query

The parameters of the Gauge Query include:

  • Query Name is what will be referenced in your block’s JavaScript code.
  • Device IDS / Tags is a device query for choosing which devices’ data you want to access.
  • Attribute is the device attribute whose value will be returned in the query. Note that if data from more than one device is being displayed, each of those devices must supply the same attribute name.
  • Duration is how far into the past you want to look at the data.
  • Aggregation determines how all the available data returned should be aggregated before being read. For example, “Mean” averages all data points together before displaying within the gauge. This field is only available if “Data Type” is set to “Historical”, and any of the following conditions applies:

    • Duration is set to anything other than “Last received data point.”
    • A device tag is supplied in the device query.
    • More than one device ID is supplied within the device query.

Data Table Queries

Data Table Queries allow you to query any data that is stored on a data table. The data table query returns an array of your data table rows where the column names are the keys you will reference in JavaScript. We automatically generate a list of columns that are available to query as well as all default row data.

Data Table Query

Building this query is done the same way as building a query in the Table: Get Rows Node, where an array of individual queries can be joined with an “OR” or “AND” operator.

HTML Configuration

HTML Configuration

Once you have built your queries, you can provide WEGnology with the custom HTML, CSS, and JavaScript to use to create your block. This is split into a “Custom Head Content” section (which is where you should place any content you would normally place in the head section of an HTML document), and a “Custom Body Content” section (where you should place any content you would normally place in the body section of an HTML document).

WEGnology uses this to content to create a full HTML document that is then rendered inside of the dashboard block. Your page contents are only loaded a single time when the dashboard is initially displayed. In order to receive your query data, your page must include JavaScript that listens to special events that WEGnology provides.

Accessing Data using Javascript

Within the context of your page, there will be a global JavaScript object called DashboardBlock available. It exposes several events that can be subscribed to from your custom JavaScript in order to receive your query data (among other things).

You can subscribe to events using the following syntax: DashboardBlock.on(‘eventName’, myCustomFunction). All events are emitted on the initial block load and then again whenever the specific action occurs. The DashboardBlock object emits the following events:

  • queryChange - emitted whenever new query results are available.
  • resize - emitted whenever the block size changes.
  • themeChange - emitted whenever the user changes the dashboard’s theme.
  • ctxChange - emitted whenever any dashboard context variable changes.
  • change - catch-all event that is emitted on any of the above changes.

All events provide the same object to your custom listener functions. It contains the following fields:

  • queries.<query-name> - The results of each query. Refer to the documentation about queries in the previous section for the specific data format of each query result.
  • size.width - The width of the dashboard block, in pixels.
  • size.height - The height of the dashboard block, in pixels.
  • dashboard.theme - The current theme of the dashboard (light or dark).
  • dashboard.time - The past state time of the dashboard, or the time at which data was last fetched, in milliseconds since epoch.
  • ctx.<variableName> - The value of any context variable on your dashboard.

The global DashboardBlock has the following methods and properties:

  • on('eventName', functionRef) - allows you to attach a listener to one of the events named above.
  • removeListener('eventName', functionRef) - allows you to remove a listener from an event.
  • input - returns the current state of the block input. This is the same as the object handed to event calls, the object described above.

Examples

The following are some examples of how to use the Custom HTML block to create various visualizations.

Using Google Charts

Custom HTML Example - Google Chart

Example Google Charts Head Content

<script
  type="text/javascript"
  src="https://www.gstatic.com/charts/loader.js"
></script>
<script type="text/javascript">
  var googleLoaded = false
  var drawChart = function() {
    if (!googleLoaded) {
      return
    }
    var data = [['Label', 'Value']]
    if (DashboardBlock.input.queries.psi) {
      data.push(['PSI', DashboardBlock.input.queries.psi.value])
    }
    data = google.visualization.arrayToDataTable(data)
    var options = {
      width: DashboardBlock.input.size.width - 5,
      height: DashboardBlock.input.size.height - 5,
      redFrom: 40,
      redTo: 50,
      yellowFrom: 25,
      yellowTo: 40,
      minorTicks: 5,
      majorTicks: ['0', '10', '20', '30', '40', '50'],
      max: 50,
    }
    let chart = new google.visualization.Gauge(
      document.getElementById('chart_div')
    )
    chart.draw(data, options)
  }

  DashboardBlock.on('change', drawChart)

  google.charts.load('current', { packages: ['gauge'] })
  google.charts.setOnLoadCallback(function() {
    googleLoaded = true
    drawChart()
  })
</script>

Example Google Charts Body Content

<div id="chart_div"></div>

Using Chart.js

Custom HTML Example - Chart.js

Example Chart.js Head Content

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

<script>
  var drawChart = function() {
    if (!DashboardBlock.input.queries.cents) return

    var data = DashboardBlock.input.queries.cents.map(function(point) {
      return point.value
    })
    var labels = DashboardBlock.input.queries.cents.map(function(point) {
      return moment(point.time).format('h:mm:ss a')
    })

    var ctx = document.getElementById('myChart').getContext('2d')
    var canvas = document.getElementById('myChart')

    canvas.width = DashboardBlock.input.size.width
    canvas.height = DashboardBlock.input.size.height
    var myChart = new Chart(ctx, {
      type: 'line',
      data: {
        labels: labels,
        datasets: [
          {
            label: 'Cents',
            data: data,
            backgroundColor: 'rgb(255, 99, 132)',
            borderColor: 'rgb(255, 99, 132)',
            borderWidth: 1,
            fill: false,
          },
        ],
      },
      options: {
        scales: {
          yAxes: [
            {
              ticks: {
                beginAtZero: true,
              },
            },
          ],
        },
      },
    })
  }
  DashboardBlock.on('dataChange', function(input) {
    console.log('datachange', input)
  })
  DashboardBlock.on('change', drawChart)
</script>

Example Chart.js Body Content

<canvas id="myChart"></canvas>

Using Google Maps

Custom HTML Example - Google Maps

Example Google Maps Head Content

<title>Simple Map</title>
<meta name="viewport" content="initial-scale=1.0" />
<meta charset="utf-8" />
<style>
  /* Always set the map height explicitly to define the size of the div
   * element that contains the map. */
  #map {
    height: 100%;
  }
  /* Optional: Makes the sample page fill the window. */
  html,
  body {
    height: 100%;
    margin: 0;
    padding: 0;
  }
</style>

Example Google Maps Body Content

<div id="map"></div>
<script>
  var map
  function initMap() {
    var drawChart = function() {
      if (!DashboardBlock.input.queries.gps) {
        return
      }
      var gps = DashboardBlock.input.queries.gps.value.split(',')
      var latLng = { lat: parseInt(gps[0]), lng: parseInt(gps[1]) }
      map = new google.maps.Map(document.getElementById('map'), {
        center: latLng,
        zoom: 7,
      })
      var marker = new google.maps.Marker({
        position: latLng,
        map: map,
        title: 'Hello World!',
      })
    }
    DashboardBlock.on('change', drawChart)
  }
</script>
<script
  src="https://maps.googleapis.com/maps/api/js?key=GOOGLE_API_KEY&callback=initMap"
  async
  defer
></script>

Using Plotly.js

'Plotly.js Example'

Example Plotly.js Head Content

<script src="https://cdn.plot.ly/plotly-2.12.1.min.js"></script>
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const chart = document.getElementById('plotly-chart');
    const layoutOptions = {
      margin: {
        b: 40, l: 40, r: 20, t: 10
      }
    };
    const configOptions = {
      responsive: true
    }
    DashboardBlock.on('change', ({ queries }) => {
      const newData = Object.entries(queries).map(([queryName, points]) => {
        return {
          x: points.map(({ time }) => time),
          y: points.map(({ value }) => value),
          type: 'bar',
          name: queryName
        }
      });
      if (!chart.data) {
        Plotly.newPlot(chart, newData, layoutOptions, configOptions);
      } else {
        Plotly.react(chart, newData, layoutOptions, configOptions);
      }
    });
  });
</script>

Example Plotly.js Body Content

<div id="plotly-chart" style="height: 100%";></div>

Creating A Simple Table

Custom HTML Example - Simple Table

Example Simple Table Head Content

<link
  rel="stylesheet"
  href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
/>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<style>
  body {
    padding: 10px;
    margin: 0px;
    background: transparent;
  }
</style>
<script type="text/javascript">
  function renderBlock(input) {
    if (DashboardBlock.input.queries.cost) {
      $('#table-body').empty()
      // loop through each point and append row to table
      input.queries.cost.forEach(function(point) {
        $('#table-body').append(`
              <tr>
              <td>${moment(point.time).format(
                'dddd, MMMM Do YYYY, h:mm:ss a'
              )}</td>
              <td>${point.value}</td>
              </tr>
          `)
      })
    }
  }

  DashboardBlock.on('queryChange', renderBlock)
</script>

Example Simple Table Body Content

<div class="container-fluid">
  <table id="table" class="table table-dark">
    <thead>
      <tr>
        <th scope="col">Time</th>
        <th scope="col">Cost</th>
      </tr>
    </thead>
    <tbody id="table-body"></tbody>
  </table>
</div>