diff options
| -rw-r--r-- | app.js | 2 | ||||
| -rw-r--r-- | public/javascripts/dash.js | 95 | ||||
| -rw-r--r-- | public/stylesheets/style.css | 4 | ||||
| -rw-r--r-- | routes/energy-usage.js | 24 | ||||
| -rw-r--r-- | routes/index.js | 29 | ||||
| -rw-r--r-- | services/device-manager.js | 18 | ||||
| -rw-r--r-- | views/index.hbs | 41 | ||||
| -rw-r--r-- | views/layout.hbs | 1 |
8 files changed, 177 insertions, 37 deletions
@@ -5,6 +5,7 @@ var cookieParser = require('cookie-parser'); var logger = require('morgan'); var indexRouter = require('./routes/index'); +var energyUsageRouter = require('./routes/energy-usage'); var app = express(); @@ -19,6 +20,7 @@ app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); +app.use('/energy-usage', energyUsageRouter); // catch 404 and forward to error handler app.use(function(req, res, next) { diff --git a/public/javascripts/dash.js b/public/javascripts/dash.js new file mode 100644 index 0000000..48b5fd5 --- /dev/null +++ b/public/javascripts/dash.js @@ -0,0 +1,95 @@ +var dash = { + pollRateMs: 1000, + pollingEnabled: true, + realtimeGuage: null, + + init: function() { + this.initRealtimeGuage(); + this.startPolling(); + }, + + initRealtimeGuage: function() { + var opts = { + angle: 0, + lineWidth: 0.2, + pointer: { + length: 0.6, + strokeWidth: 0.035, + color: '#000000' + }, + limitMax: true, + limitMin: true, + generateGradient: true, + highDpiSupport: true, + staticLabels: { + font: "12px sans-serif", + labels: [500, 1500, 3000] + }, + staticZones: [ + { strokeStyle: "#30B32D", min: 0, max: 500 }, + { strokeStyle: "#FFDD00", min: 500, max: 1500 }, + { strokeStyle: "#F03E3E", min: 1500, max: 3000 } + ] + }; + var target = document.getElementById('rtu-gauge'); + + dash.realtimeGuage = new Gauge(target).setOptions(opts); + dash.realtimeGuage.maxValue = 3000; + dash.realtimeGuage.setMinValue(0); + dash.realtimeGuage.animationSpeed = 32; + dash.realtimeGuage.set(500); + }, + + // TODO - should probably use websockets + poll: function() { + if(this.pollingEnabled) { + $.ajax({ + url: "/energy-usage/1/realtime", + type: "GET", + success: function(data) { + dash.refreshDashboard(data); + }, + dataType: "json", + complete: setTimeout(function() {dash.poll()}, 1000), + timeout: 2000 + }); + } + }, + + refreshDashboard: function(realtime) { + + var power = Math.round(realtime.power); + var current = realtime.current.toFixed(2); + var voltage = Math.round(realtime.voltage); + + this.realtimeGuage.set(power); + // might switch to Vue.js is this gets tedious + $("#rtu-power").text(power + " kW") + $("#rtu-current").text(current + " A") + $("#rtu-voltage").text(voltage + " V") + + }, + + startPolling: function() { + this.pollingEnabled = true; + this.poll(); + }, + + stopPolling: function() { + this.pollingEnabled = false; + }, + +} + + +$(document).ready(function () { + dash.init(); + $("#toggle-polling").change(function() { + if(this.checked) { + dash.startPolling(); + } + else { + dash.stopPolling(); + } + }) +}); diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index b8545e9..320eb01 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -1,3 +1,7 @@ footer ul { margin-bottom: 0px; } + +.page-header { + margin: 10px 0 20px; +}
\ No newline at end of file diff --git a/routes/energy-usage.js b/routes/energy-usage.js new file mode 100644 index 0000000..847248d --- /dev/null +++ b/routes/energy-usage.js @@ -0,0 +1,24 @@ +const express = require('express'); +const router = express.Router(); + +const deviceManager = require('../services/device-manager'); + +router.get('/:deviceId/realtime', function(req, res, next) { + + let deviceId = req.params.deviceId; + + let realtimeUsage = {}; + // TODO - cache results with a short TTL so we don't hammer the plug if multiple clients are requesting data + deviceManager.getDevice(deviceId).emeter.getRealtime().then(response => { + + // Voltage seems to be reported as its peak to peak voltage, not RMS. + // Show the RMS value since thats what would you expect to see. + // i.e. 220v not 310v (in the U.K) + response.voltage = response.voltage / Math.sqrt(2); + + res.json(response); + }); + +}); + +module.exports = router;
\ No newline at end of file diff --git a/routes/index.js b/routes/index.js index d1d0519..1dba277 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,30 +1,13 @@ -var express = require('express'); -var router = express.Router(); +const express = require('express'); +const router = express.Router(); -const { Client } = require('tplink-smarthome-api'); - -const client = new Client(); -var devices = []; - -client.startDiscovery({deviceTypes: ['plug']}).on('plug-new', plug => { - console.log('Found device: ' + plug.alias + ' [' + plug.deviceId + ']'); - devices.push(plug); -}) +const deviceManager = require('../services/device-manager'); router.get('/', function(req, res, next) { - let realtimeUsage = {}; - devices[0].emeter.getRealtime().then(response => { - - realtimeUsage.power = Math.round(response.power); - realtimeUsage.current = response.current.toFixed(2); - realtimeUsage.voltage = Math.round(response.voltage); - - res.render('index',{ - device: devices[0], - devices: devices, - realtimeUsage: realtimeUsage - }); + res.render('index', { + device: deviceManager.getDevice(), + devices: deviceManager.getAllDevices() }); }); diff --git a/services/device-manager.js b/services/device-manager.js new file mode 100644 index 0000000..3ed4a26 --- /dev/null +++ b/services/device-manager.js @@ -0,0 +1,18 @@ +const { Client } = require('tplink-smarthome-api'); + +const client = new Client(); +var devices = []; + +client.startDiscovery({deviceTypes: ['plug']}).on('plug-new', plug => { + console.log('Found device: ' + plug.alias + ' [' + plug.deviceId + ']'); + devices.push(plug); +}) + +module.exports.getDevice = function(deviceId) { + // TODO - get by id + return devices[0]; +} + +module.exports.getAllDevices = function() { + return devices; +}
\ No newline at end of file diff --git a/views/index.hbs b/views/index.hbs index df75747..8885933 100644 --- a/views/index.hbs +++ b/views/index.hbs @@ -30,14 +30,24 @@ </div> <div class="right_col" role="main"> + <div class="page-header"> - <h1> - <i class="fa fa-plug"></i> {{device.alias}} {{#device.relayState}} - <span class="label label-success">ON</span> - {{/device.relayState}} {{^device.relayState}} - <span class="label label-danger">OFF</span> - {{/device.relayState}} - </h1> + <div class="row"> + <div class="col-sm-8"> + <h1> + <i class="fa fa-plug"></i> {{device.alias}} {{#device.relayState}} + <span class="label label-success">ON</span> + {{/device.relayState}} {{^device.relayState}} + <span class="label label-danger">OFF</span> + {{/device.relayState}} + </h1> + </div> + <div class="col-sm-4"> + <h2 class="pull-right"> + <input id="toggle-polling" class="form-control" type="checkbox" checked data-toggle="toggle" data-on="Auto refresh on" data-off="Auto refresh off" data-offstyle="danger"> + </h2> + </div> + </div> </div> <div class="row"> @@ -54,13 +64,13 @@ <div class="x_content"> <div class="row"> <h1 class="text-center"> - <strong>{{realtimeUsage.power}} kW</strong> + <strong id="rtu-power">- kW</strong> </h1> </div> <div class="row text-center"> <div> - <canvas id="realtime-gauge"></canvas> + <canvas id="rtu-gauge"></canvas> </div> </div> @@ -68,12 +78,12 @@ <div class="row"> <div class="col-md-6 col-xs-6 text-center"> <h1> - <strong>{{realtimeUsage.current}} A</strong> + <strong id="rtu-current">- A</strong> </h1> </div> <div class="col-md-6 col-xs-6 text-center"> <h1> - <strong>{{realtimeUsage.voltage}} V</strong> + <strong id="rtu-voltage">- V</strong> </h1> </div> </div> @@ -105,9 +115,12 @@ <script src="https://getbootstrap.com/assets/js/vendor/popper.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script> - <script src="http://bernii.github.io/gauge.js/dist/gauge.min.js"></script> + <script src="https://bernii.github.io/gauge.js/dist/gauge.min.js"></script> + <script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script> + + <script src="/javascripts/dash.js"></script> - <script> + {{!-- <script> $(document).ready(function () { var opts = { @@ -140,4 +153,4 @@ gauge.animationSpeed = 1; gauge.set({{ realtimeUsage.power }}); }) - </script>
\ No newline at end of file + </script> --}}
\ No newline at end of file diff --git a/views/layout.hbs b/views/layout.hbs index 2f78105..c1c5192 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -12,6 +12,7 @@ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> <link href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' /> <link href='https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.4.0/css/custom.min.css' rel='stylesheet' /> + <link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet"> <link href='/stylesheets/style.css' rel='stylesheet' /> |