Skip to content
Snippets Groups Projects
Select Git revision
  • fc91dce44b38f0206342e9e82179ea2ddf82cc56
  • master default protected
2 results

airqmon.js

Blame
  • airqmon.js 6.43 KiB
    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³"},
        "nc1": {name: "NC ≥0.5µm", unit: "/100cm³"},
        "nc2p5": {name: "NC ≥1.0µ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, 0.75)',
            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);
            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 my-2 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 my-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');
    };