var gauge = new Gauge($('#gauge')[0]); var websock; var timeseries = {}; var charts = {}; $(document).ready(function(event) { setupGauge('staticZones'); //setupGauge('percentColors'); startWebsocket(); }); var metrics = { "iaq": {name: "IAQ", has_accuracy: true, decimals: 0}, "siaq": {name: "Static IAQ", has_accuracy: true, decimals: 0}, "eco2": {name: "CO2", unit: " ppm", has_accuracy: true, decimals: 0}, "bvoc": {name: "Breath VOC", unit: " ppm", has_accuracy: true, decimals: 3}, "pm1": {name: "PM ≤1.0µm", unit: " μg/m³"}, "pm2p5": {name: "PM ≤2.5µm", unit: " μg/m³"}, "pm10": {name: "PM ≤10µm", unit: " μg/m³"}, "nc0p3": {name: "NC ≥0.3µm", unit: "/100cm³"}, "nc0p5": {name: "NC ≥0.5µm", unit: "/100cm³"}, "nc1": {name: "NC ≥1.0µm", unit: "/100cm³"}, "nc2p5": {name: "NC ≥2.5µm", unit: "/100cm³"}, "nc5": {name: "NC ≥5.0µm", unit: "/100cm³"}, "nc10": {name: "NC ≥10µm", unit: "/100cm³"}, "temperature": {name: "Temperature", unit: " °C", decimals: 1}, "humidity": {name: "Humidity", unit: "%", decimals: 1}, "pressure": {name: "Pressure", unit: " hPa", decimals: 0} } var accuracyStatus = { 0: {name: "Stabilizing", class: "danger"}, 1: {name: "Uncertain", class: "warning"}, 2: {name: "Calibrating", class: "primary"}, 3: {name: "Calibrated", class: "success"} } var gaugeOptions = { angle: 0, // The span of the gauge arc lineWidth: 0.45, // The line thickness radiusScale: 1, // Relative radius pointer: { length: 0.5, // // Relative to gauge radius strokeWidth: 0.100, // The thickness color: '#000000' // Fill color }, colorStart: '#6F6EA0', // Colors colorStop: '#C0C0DB', // just experiment with them strokeColor: '#EEEEEE', // to see which ones work best for you generateGradient: true, highDpiSupport: true, // High resolution support staticLabels: { font: "10px sans-serif", // Specifies font labels: [50, 100, 150, 200, 250, 350, 500], // Print labels at these values color: "#000000", // Optional: Label text color fractionDigits: 0 // Optional: Numerical precision. 0=round off. } }; var gaugeModes = { staticZones: [ {strokeStyle: "#01E400", min: 0, max: 50}, // Bright Green {strokeStyle: "#92D050", min: 51, max: 100}, // Green {strokeStyle: "#FFFF00", min: 101, max: 150}, // Yellow {strokeStyle: "#FF7E00", min: 151, max: 200}, // Orange {strokeStyle: "#FF0000", min: 201, max: 250}, // Red {strokeStyle: "#99004C", min: 251, max: 350}, // Violet {strokeStyle: "#663300", min: 351, max: 500} // Brown ], percentColors: [ [0.0, "#01E400" ], [0.1, "#92D050" ], [0.2, "#FFFF00"], [0.3, "#FF7E00"], [0.4, "#FF0000"], [0.5, "#99004C"], [0.7, "#663300"] ] } var chartOptions = { responsive: true, millisPerPixel: 100, grid: { fillStyle:'rgba(255,255,255, 1)', strokeStyle:'rgba(128,128,128, 0.10)', verticalSections: 5 }, labels: { fillStyle:'rgba(0,0,0,0.75)' } } var lineOptions = { lineWidth: 2, strokeStyle:'#00ff00' } function setupGauge(mode) { var objColor = {} objColor[mode] = gaugeModes[mode]; var opt = Object.assign(gaugeOptions, objColor); gauge.setOptions(opt); gauge.setMinValue(0); gauge.maxValue = 500; gauge.animationSpeed = 128; gauge.set(0); } function startWebsocket() { websock = new WebSocket('ws://' + window.location.hostname + ':81/'); websock.onopen = function(evt) { console.log('websock open'); $('#wsSpinner').invisible(); }; websock.onclose = function(evt) { console.log('websock close'); websock = null; $('#wsSpinner').visible(); setTimeout(startWebsocket, 1000); }; websock.onerror = function(evt) { console.log(evt); }; websock.onmessage = function(evt) { data = JSON.parse(evt.data); //console.log(data); if (data !== null) { handleWebsocketMessage(data); } }; } function handleWebsocketMessage(data) { if ('siaq' in data) { gauge.set(data.siaq); } $.each(metrics, function(name, sensor) { if ('has_accuracy' in metrics[name]) { updateMetric(name, data[name], data[name+'_acc']); } else { updateMetric(name, data[name], false); } }); } function updateMetric(name, value, accuracy) { var numericValue = value; if ('decimals' in metrics[name]) { value = value.toFixed(metrics[name].decimals); } if ('unit' in metrics[name]) { value += metrics[name].unit; } if (accuracy !== false && accuracy < 3) { value = '<span class="badge bg-'+accuracyStatus[accuracy].class+'">'+accuracyStatus[accuracy].name+'</span> '+value; } if(!$('#metric_'+name).length && name in metrics) { timeseries[name] = new TimeSeries(); timeseries[name].append(new Date().getTime(), numericValue); var metric = $('<li class="list-group-item list-group-item-action" id="metric_'+name+'">'+ '<div class="row rowMetric">'+ '<div class="d-flex w-100 justify-content-between">'+ '<div class="sensorName">'+metrics[name].name+'</div>'+ '<div class="sensorValue">'+value+'</div>'+ '</div>'+ '</div>'+ '</li>)'); metric.find('.rowMetric').on('click', function() { toggleChart(name); }); $('#metrics').append(metric); } else { $('#metric_'+name+' .sensorValue').html(value); timeseries[name].append(new Date().getTime(), numericValue); } } function toggleChart(name) { if(!$('#chart_'+name).length) { charts[name] = new SmoothieChart(chartOptions); charts[name].addTimeSeries(timeseries[name], lineOptions); $('#metric_'+name).append($('<div class="row rowChart">'+ '<div class="chart mt-2"><canvas id="chart_'+name+'"></canvas></div>'+ '</div>')); charts[name].streamTo(document.getElementById("chart_"+name), 1000); } else { charts[name].removeTimeSeries(timeseries[name]); charts[name].stop(); delete charts[name]; $('#metric_'+name+' .rowChart').remove(); } } jQuery.fn.visible = function() { return this.css('visibility', 'visible'); }; jQuery.fn.invisible = function() { return this.css('visibility', 'hidden'); };