SondeHub Listeners

HTML

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>SondeHub Station Statistics</title>
    <meta name="description" content="Live graph showing breakdown of programs and versions contributing telemetry to the SondeHub database." />
    <meta name="author" content="Luke Prior" />
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1" />
    <script type="text/javascript" language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.js"></script>
  </head>

  <body>
    <div id="container">
      <canvas id="chartJSContainer" style="display: none;"></canvas>
      <div id="loadingGif" style="display: block;">
        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(255, 255, 255, 0); display: block; shape-rendering: auto;" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
          <circle cx="50" cy="50" r="32" stroke-width="8" stroke="#00a3d3" stroke-dasharray="50.26548245743669 50.26548245743669" fill="none" stroke-linecap="round">
            <animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="0 50 50;360 50 50"></animateTransform>
          </circle>
        </svg>
      </div>
      <div>
        <button id="update" class="button">MODE</button>
        <button id="hide" class="button">VERSIONS</button>
      </div>
    </div>
  </body>

</html>

CSS

.button {
  background-color: white;
  color: black;
  padding: 6px 8px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 10px;
  margin: 3px 2px;
  transition-duration: 0.4s;
  cursor: pointer;
  border: 2px solid #555555;
}

.button:hover {
  background-color: #555555;
  color: white;
}

#container {
    width: 100%;
    max-width: 90vh;
    right: 50%;
    bottom: 50%;
    transform: translateX(50%) translateY(50%);
    position: absolute;
}

JavaScript

var count = [];
var countNested = [];
var countUnique = [];
var countNestedUnique = [];
var empty = [];
var countSelected = true;
var nestedSelected = false;
var stationCount = '';

var programs = [];
var versions = [];
var versionPrograms = [];
var colours = ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff33', '#a65628', '#f781bf', '#999999'];
var excluded = ['Sonde Tracker', 'TTGO EMA DTCEA', 'TTGO WX'];

var visibility = {};

var getJSON = function(url, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.responseType = 'json';
  xhr.onload = function() {
    var status = xhr.status;
    if (status === 200) {
      callback(null, xhr.response);
    } else {
      callback(status, xhr.response);
    }
  };
  xhr.send();
};

function resizeArray(array, size) {
  var dataNew = [];
  var sum = array.reduce((a, b) => a + b);

  array.forEach(function(item, index) {
    var rounded = Math.round(item / sum * size);
    dataNew.push(rounded);
  });

  var difference = dataNew.reduce((a, b) => a + b) - size;
  dataNew[0] = dataNew[0] - difference;

  return dataNew;
}

const countPlugin = {
  id: 'doughnut-centertext',
  beforeDraw: function(chart) {
    if (chart.config.options.elements.center) {
      // Get ctx from string
      var ctx = chart.ctx;

      var innerRadius = chart._metasets[chart._metasets.length - 2].controller.innerRadius;
      if (chart._metasets[chart._metasets.length - 1].controller.innerRadius > 0) {
        innerRadius = chart._metasets[chart._metasets.length - 1].controller.innerRadius;
      }

      // Get options from the center object in options
      var centerConfig = chart.config.options.elements.center;
      var fontStyle = centerConfig.fontStyle || 'Arial';
      var txt = centerConfig.text;
      var color = centerConfig.color || '#000';
      var maxFontSize = centerConfig.maxFontSize || 75;
      var sidePadding = centerConfig.sidePadding || 20;
      var sidePaddingCalculated =...