aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Barnett <noreply@jamesbarnett.xyz>2018-03-30 22:02:12 +0100
committerJames Barnett <noreply@jamesbarnett.xyz>2018-03-30 22:02:12 +0100
commit914d60ca25def8805b7fb2dafcab17474e08cc87 (patch)
tree53ad95f040157f442aa62d1e648f75896e26a106
parentecc769dab59d427c1fa5dc8856dd32068b4ba6db (diff)
downloadtplink-energy-monitor-914d60ca25def8805b7fb2dafcab17474e08cc87.tar.xz
tplink-energy-monitor-914d60ca25def8805b7fb2dafcab17474e08cc87.zip
Poll for realtime usage updates
-rw-r--r--app.js2
-rw-r--r--public/javascripts/dash.js95
-rw-r--r--public/stylesheets/style.css4
-rw-r--r--routes/energy-usage.js24
-rw-r--r--routes/index.js29
-rw-r--r--services/device-manager.js18
-rw-r--r--views/index.hbs41
-rw-r--r--views/layout.hbs1
8 files changed, 177 insertions, 37 deletions
diff --git a/app.js b/app.js
index a503c42..c42b3bc 100644
--- a/app.js
+++ b/app.js
@@ -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' />