aboutsummaryrefslogtreecommitdiff
path: root/routes
diff options
context:
space:
mode:
authorJames Barnett <noreply@jamesbarnett.xyz>2018-04-07 18:49:48 +0100
committerJames Barnett <noreply@jamesbarnett.xyz>2018-04-07 18:49:48 +0100
commit24b4a39fce61dd9bd8ab7757f3bbda0636adc8c9 (patch)
treeb4ce61e16a66c2a43e40b8f78cc8431af7ef2d3a /routes
parent954aff49707738e660e92e0418c31b1ec78a85a8 (diff)
downloadtplink-energy-monitor-24b4a39fce61dd9bd8ab7757f3bbda0636adc8c9.tar.xz
tplink-energy-monitor-24b4a39fce61dd9bd8ab7757f3bbda0636adc8c9.zip
Switch from http polling to data push via websockets
Diffstat (limited to 'routes')
-rw-r--r--routes/energy-usage.js166
-rw-r--r--routes/power-state.js23
-rw-r--r--routes/ws.js25
3 files changed, 25 insertions, 189 deletions
diff --git a/routes/energy-usage.js b/routes/energy-usage.js
deleted file mode 100644
index eb21eb9..0000000
--- a/routes/energy-usage.js
+++ /dev/null
@@ -1,166 +0,0 @@
-const express = require('express');
-const router = express.Router();
-
-const deviceManager = require('../services/device-manager');
-const moment = require('moment');
-
-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 value, 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);
- });
-
-});
-
-router.get('/:deviceId/day-stats', function(req, res, next) {
-
- let deviceId = req.params.deviceId;
-
- // Get last x days
- let totalDaysRequired = 30; // TODO currently only works for up to 2 months spans
- let currentMoment = moment();
- let previousMoment = moment().subtract(totalDaysRequired, 'days');
-
- // Month + 1 as the API months are index 1 based.
- deviceManager.getDevice(deviceId).emeter.getDayStats(currentMoment.year(), currentMoment.month() +1).then(currentPeriodStats => {
-
- // Check if we also need the previous month to meet the required total number of samples
- if(currentMoment.month() !== previousMoment.month()) {
-
- // Get previous month. This currently wont work if the previousMoment is more than 1 month before the currentMoment (see above)
- deviceManager.getDevice(deviceId).emeter.getDayStats(previousMoment.year(), previousMoment.month() +1).then(previousPeriodStats => {
-
- let currentMonthStats = fillMissingDays(currentPeriodStats, currentMoment);
- let previousMonthStats = fillMissingDays(previousPeriodStats, previousMoment);
- let combinedStats = previousMonthStats.concat(currentMonthStats);
-
- res.json(trimStatResults(combinedStats, totalDaysRequired));
-
- });
- }
- else {
- let dayStats = fillMissingDays(currentPeriodStats, currentMoment);
-
- res.json(trimStatResults(dayStats, totalDaysRequired));
- }
-
- });
-
-});
-
-router.get('/:deviceId/month-stats', function(req, res, next) {
- let deviceId = req.params.deviceId;
-
- // Get last x months
- let totalMonthsRequired = 12; // TODO currently only works for up to 14 month (2 year) spans
- let currentMoment = moment();
- let previousMoment = moment().subtract(totalMonthsRequired, 'months');
-
- deviceManager.getDevice(deviceId).emeter.getMonthStats(currentMoment.year()).then(currentPeriodStats => {
-
- // Check if we also need the previous year to meet the required total number of samples
- if(currentMoment.month() + 1 < totalMonthsRequired) {
-
- // Get previous year (assuming the totalMonthsRequired limit described above).
- deviceManager.getDevice(deviceId).emeter.getMonthStats(previousMoment.year()).then(previousPeriodStats => {
-
- let currentYearStats = fillMissingMonths(currentPeriodStats, currentMoment);
- let previousYearStats = fillMissingMonths(previousPeriodStats, previousMoment);
- let combinedStats = previousYearStats.concat(currentYearStats);
-
- res.json(trimStatResults(combinedStats, totalMonthsRequired));
-
- });
- }
- else {
- let monthStats = fillMissingMonths(currentPeriodStats, currentMoment);
-
- res.json(trimStatResults(monthStats, totalMonthsRequired));
- }
-
- });
-
-});
-
-function fillMissingDays(sparseDayStats, statsMoment) {
- let denseDayStats = [];
-
- let totalDays;
- // If these stats are for the current month, fill up to the current day of the month
- // Otherwise fill the whole month
- if(moment().month() === statsMoment.month()) {
- totalDays = statsMoment.date();
- }
- else {
- totalDays = statsMoment.daysInMonth();
- }
-
- Array.from({length: totalDays}, (x,i) => i + 1).forEach(d => {
-
- let stat = sparseDayStats.day_list.find(i => i.day === d);
-
- if(stat === undefined) {
- denseDayStats.push({
- year: statsMoment.year(),
- month: statsMoment.month() +1,
- day: d,
- energy: 0
- })
- }
- else {
- denseDayStats.push(stat);
- }
-
- });
-
- return denseDayStats;
-}
-
-function fillMissingMonths(sparseMonthStats, statsMoment) {
- let denseMonthStats = [];
-
- let maxMonths;
- // Dont fill in months which exist in the future
- if(statsMoment.year() === moment().year()) {
- maxMonths = moment().month() + 1; // API months are 1 based
- }
- else {
- maxMonths = 12;
- }
-
- // Fill in any missing months up to the max amount
- Array.from({length: maxMonths}, (x,i) => i + 1).forEach(m => {
-
- let stat = sparseMonthStats.month_list.find(i => i.month === m);
-
- if(stat === undefined) {
- denseMonthStats.push({
- year: statsMoment.year(),
- month: m,
- energy: 0
- })
- }
- else {
- denseMonthStats.push(stat);
- }
-
- });
-
- return denseMonthStats;
-}
-
-function trimStatResults(stats, maxSamples) {
- return stats.splice(stats.length - maxSamples, stats.length);
-}
-
-module.exports = router; \ No newline at end of file
diff --git a/routes/power-state.js b/routes/power-state.js
deleted file mode 100644
index 6333863..0000000
--- a/routes/power-state.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const express = require('express');
-const router = express.Router();
-
-const deviceManager = require('../services/device-manager');
-const moment = require('moment');
-
-router.get('/:deviceId', function(req, res, next) {
-
- let deviceId = req.params.deviceId;
-
- deviceManager.getDevice(deviceId).getSysInfo().then(response => {
-
- let powerState = {
- isOn: (response.relay_state === 1),
- uptime: response.on_time
- };
-
- res.json(powerState);
- });
-
-});
-
-module.exports = router; \ No newline at end of file
diff --git a/routes/ws.js b/routes/ws.js
new file mode 100644
index 0000000..dc22df2
--- /dev/null
+++ b/routes/ws.js
@@ -0,0 +1,25 @@
+const express = require('express');
+const router = express.Router();
+
+const deviceManager = require('../services/device-manager');
+const dataFetcher = require('../services/data-fetcher');
+const dataBroadcaster = require('../services/data-broadcaster');
+
+router.ws('/', function(ws, req) {
+
+ ws.on('message', msg => {
+
+ // Latest data is always pushed out to clients, but clients can also request cached data at any time.
+ if(msg === 'getCachedData') {
+ let cachedData = dataFetcher.getCachedData();
+
+ ws.send(dataBroadcaster.generatePayload('realtimeUsage', cachedData.realtimeUsage));
+ ws.send(dataBroadcaster.generatePayload('dailyUsage', cachedData.dailyUsage));
+ ws.send(dataBroadcaster.generatePayload('monthlyUsage', cachedData.monthlyUsage));
+ ws.send(dataBroadcaster.generatePayload('powerState', cachedData.powerState));
+ }
+ });
+
+});
+
+module.exports = router;