aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Barnett <noreply@jamesbarnett.xyz>2018-04-07 21:43:17 +0100
committerJames Barnett <noreply@jamesbarnett.xyz>2018-04-07 21:43:17 +0100
commit1301d90e93af799a9054f133847ebf3cbda15f9d (patch)
tree7c9e12431c63a7a52523d2cd1f15e144dcaf680f
parent24b4a39fce61dd9bd8ab7757f3bbda0636adc8c9 (diff)
downloadtplink-energy-monitor-1301d90e93af799a9054f133847ebf3cbda15f9d.tar.xz
tplink-energy-monitor-1301d90e93af799a9054f133847ebf3cbda15f9d.zip
Add support for switching between multiple plugs
-rw-r--r--public/javascripts/dash.js51
-rw-r--r--routes/index.js20
-rw-r--r--routes/ws.js15
-rw-r--r--services/data-broadcaster.js19
-rw-r--r--services/data-fetcher.js247
-rw-r--r--services/device-manager.js7
-rw-r--r--views/index.hbs9
7 files changed, 224 insertions, 144 deletions
diff --git a/public/javascripts/dash.js b/public/javascripts/dash.js
index 72b0b5a..7a8207c 100644
--- a/public/javascripts/dash.js
+++ b/public/javascripts/dash.js
@@ -1,4 +1,6 @@
var dash = {
+ deviceId: null,
+
realtimeGauge: null,
realtimeTrendChart: null,
realtimeTrendLastSample: 0,
@@ -6,7 +8,11 @@ var dash = {
dailyUsageChart: null,
monthlyUsageChart: null,
- init: function() {
+ init: function(deviceId) {
+ this.deviceId = deviceId;
+
+ $('#' + deviceId).addClass('active');
+
this.initRealtimeGauge();
this.initRealtimeTrendChart();
this.initDailyUsageChart();
@@ -16,29 +22,35 @@ var dash = {
},
initWsConnection: function() {
- var ws = new WebSocket('ws://192.168.1.8:3000/ws');
+ var wsUri = 'ws://' + window.location.host + '/ws'
+ var ws = new WebSocket(wsUri);
ws.onopen = function () {
console.log('Websocket connection established');
- ws.send('getCachedData');
+ ws.send(JSON.stringify(
+ {
+ requestType: 'getCachedData',
+ deviceId: dash.deviceId
+ }
+ ));
}
ws.onmessage = this.wsMessageHandler;
},
wsMessageHandler: function(messageEvent) {
let message = JSON.parse(messageEvent.data);
- console.log(message);
-
- if(message.dataType === 'realtimeUsage') {
- dash.refreshRealtimeDisplay(message.data);
- }
- else if(message.dataType === 'dailyUsage') {
- dash.parseDailyUsageData(message.data);
- }
- else if(message.dataType === 'monthlyUsage') {
- dash.parseMonthlyUsageData(message.data);
- }
- else if(message.dataType === 'powerState') {
- dash.refreshPowerState(message.data);
+ if(message.deviceId === dash.deviceId) {
+ if(message.dataType === 'realtimeUsage') {
+ dash.refreshRealtimeDisplay(message.data);
+ }
+ else if(message.dataType === 'dailyUsage') {
+ dash.parseDailyUsageData(message.data);
+ }
+ else if(message.dataType === 'monthlyUsage') {
+ dash.parseMonthlyUsageData(message.data);
+ }
+ else if(message.dataType === 'powerState') {
+ dash.refreshPowerState(message.data);
+ }
}
},
@@ -289,10 +301,3 @@ var dash = {
},
};
-
-
-$(document).ready(function () {
-
- dash.init();
-
-});
diff --git a/routes/index.js b/routes/index.js
index 4909ff8..12c62cd 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -5,11 +5,27 @@ const deviceManager = require('../services/device-manager');
router.get('/', function(req, res, next) {
+ let deviceId = sortDevices(deviceManager.getAllDevices())[0].deviceId;
+
+ res.redirect('/' + deviceId);
+
+});
+
+router.get('/:deviceId', function(req, res, next) {
+
+ let deviceId = req.params.deviceId;
+
res.render('index', {
- device: deviceManager.getDevice(),
- devices: deviceManager.getAllDevices()
+ device: deviceManager.getDevice(deviceId),
+ devices: sortDevices(deviceManager.getAllDevices())
});
});
+function sortDevices(devices) {
+ return devices.slice().sort((a, b) => {
+ return a.alias.toLowerCase().localeCompare(b.alias.toLowerCase())
+ })
+}
+
module.exports = router;
diff --git a/routes/ws.js b/routes/ws.js
index dc22df2..08b0a18 100644
--- a/routes/ws.js
+++ b/routes/ws.js
@@ -9,14 +9,17 @@ router.ws('/', function(ws, req) {
ws.on('message', msg => {
+ let message = JSON.parse(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();
+ if(message.requestType === 'getCachedData') {
+ let deviceId = message.deviceId;
+ let cachedData = dataFetcher.getCachedData(deviceId);
- 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));
+ ws.send(dataBroadcaster.generatePayload('realtimeUsage', deviceId, cachedData.realtimeUsage));
+ ws.send(dataBroadcaster.generatePayload('dailyUsage', deviceId, cachedData.dailyUsage));
+ ws.send(dataBroadcaster.generatePayload('monthlyUsage', deviceId, cachedData.monthlyUsage));
+ ws.send(dataBroadcaster.generatePayload('powerState', deviceId, cachedData.powerState));
}
});
diff --git a/services/data-broadcaster.js b/services/data-broadcaster.js
index a49f4a7..9e0c5d0 100644
--- a/services/data-broadcaster.js
+++ b/services/data-broadcaster.js
@@ -1,19 +1,19 @@
const app = require('../app');
-function broadcastRealtimeUsageUpdate(data) {
- broadcast(generatePayload('realtimeUsage', data));
+function broadcastRealtimeUsageUpdate(deviceId, data) {
+ broadcast(generatePayload('realtimeUsage', deviceId, data));
}
-function broadcastDailyUsageUpdate(data) {
- broadcast(generatePayload('dailyUsage', data));
+function broadcastDailyUsageUpdate(deviceId, data) {
+ broadcast(generatePayload('dailyUsage', deviceId, data));
}
-function broadcastMonthlyUsageUpdate(data) {
- broadcast(generatePayload('monthlyUsage', data));
+function broadcastMonthlyUsageUpdate(deviceId, data) {
+ broadcast(generatePayload('monthlyUsage', deviceId, data));
}
-function broadcastPowerStateUpdate(data) {
- broadcast(generatePayload('powersState', data));
+function broadcastPowerStateUpdate(deviceId, data) {
+ broadcast(generatePayload('powersState', deviceId, data));
}
function broadcast(payload) {
@@ -22,10 +22,11 @@ function broadcast(payload) {
})
}
-function generatePayload(dataType, data) {
+function generatePayload(dataType, deviceId, data) {
let payload = {
dataType: dataType,
+ deviceId: deviceId,
data: data
}
diff --git a/services/data-fetcher.js b/services/data-fetcher.js
index 93f5020..7ef3594 100644
--- a/services/data-fetcher.js
+++ b/services/data-fetcher.js
@@ -10,31 +10,34 @@ setTimeout(function() {
fetchDailyUsage();
fetchMonthlyUsage();
fetchPowerState();
-}, 5000)
+}, 2000)
-let cachedRealtimeUsageData = {};
-let cachedDailyUsageData = {};
-let cachedMonthlyUsageData = {};
-let cachedPowerState = {};
+let cachedRealtimeUsageData = [];
+let cachedDailyUsageData = [];
+let cachedMonthlyUsageData = [];
+let cachedPowerState = [];
function fetchRealtimeUsage() {
- let deviceId = 1; // TODO
+ if(app.getWsClientCount() > 0 || cachedRealtimeUsageData.length === 0) {
- console.log('connected clients', app.getWsClientCount());
- if(app.getWsClientCount() > 0) {
- deviceManager.getDevice(deviceId).emeter.getRealtime().then(response => {
+ deviceManager.getAllDevices().forEach(device => {
- // 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);
+ let deviceId = device.deviceId;
+ device.emeter.getRealtime().then(response => {
- cachedRealtimeUsageData = response;
-
- dataBroadcaster.broadcastRealtimeUsageUpdate(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);
+ updateCache(cachedRealtimeUsageData, deviceId, response);
+
+ dataBroadcaster.broadcastRealtimeUsageUpdate(deviceId, response);
+ });
+
});
+
}
setTimeout(fetchRealtimeUsage, 1000);
@@ -42,107 +45,127 @@ function fetchRealtimeUsage() {
function fetchDailyUsage() {
- let deviceId = 1;
-
- // 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()) {
+ if(app.getWsClientCount() > 0 || cachedDailyUsageData.length === 0) {
+
+ // 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.getAllDevices().forEach(device => {
+
+ let deviceId = device.deviceId;
+ device.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)
+ device.emeter.getDayStats(previousMoment.year(), previousMoment.month() +1).then(previousPeriodStats => {
+
+ let currentMonthStats = fillMissingDays(currentPeriodStats, currentMoment);
+ let previousMonthStats = fillMissingDays(previousPeriodStats, previousMoment);
+ let combinedStats = previousMonthStats.concat(currentMonthStats);
+
+ let result = trimStatResults(combinedStats, totalDaysRequired);
+
+ updateCache(cachedDailyUsageData, deviceId, result);
+
+ dataBroadcaster.broadcastDailyUsageUpdate(deviceId, result);
+
+ });
+ }
+ else {
+ let dayStats = fillMissingDays(currentPeriodStats, currentMoment);
- // 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);
-
- let result = trimStatResults(combinedStats, totalDaysRequired);
-
- cachedDailyUsageData = result;
-
- dataBroadcaster.broadcastDailyUsageUpdate(result);
-
+ let result = trimStatResults(dayStats, totalDaysRequired);
+ updateCache(cachedDailyUsageData, deviceId, result);
+
+ dataBroadcaster.broadcastDailyUsageUpdate(deviceId, result);
+ }
+
});
- }
- else {
- let dayStats = fillMissingDays(currentPeriodStats, currentMoment);
-
- let result = trimStatResults(dayStats, totalDaysRequired);
- cachedDailyUsageData = result;
- dataBroadcaster.broadcastDailyUsageUpdate(result);
- }
+ });
- });
+ }
setTimeout(fetchDailyUsage, 300000); // 5 mins;
}
function fetchMonthlyUsage() {
- let deviceId = 1;
-
- // 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);
-
- let result = trimStatResults(combinedStats, totalMonthsRequired);
-
- cachedMonthlyUsageData = result;
-
- dataBroadcaster.broadcastMonthlyUsageUpdate(result);
-
+ if(app.getWsClientCount() > 0 || cachedMonthlyUsageData.length === 0) {
+
+ // 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.getAllDevices().forEach(device => {
+
+ let deviceId = device.deviceId;
+ device.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).
+ device.emeter.getMonthStats(previousMoment.year()).then(previousPeriodStats => {
+
+ let currentYearStats = fillMissingMonths(currentPeriodStats, currentMoment);
+ let previousYearStats = fillMissingMonths(previousPeriodStats, previousMoment);
+ let combinedStats = previousYearStats.concat(currentYearStats);
+
+ let result = trimStatResults(combinedStats, totalMonthsRequired);
+
+ updateCache(cachedMonthlyUsageData, deviceId, result);
+
+ dataBroadcaster.broadcastMonthlyUsageUpdate(deviceId, result);
+
+ });
+ }
+ else {
+ let monthStats = fillMissingMonths(currentPeriodStats, currentMoment);
+
+ let result = trimStatResults(monthStats, totalMonthsRequired);
+
+ updateCache(cachedMonthlyUsageData, deviceId, result);
+
+ dataBroadcaster.broadcastMonthlyUsageUpdate(deviceId, result);
+ }
+
});
- }
- else {
- let monthStats = fillMissingMonths(currentPeriodStats, currentMoment);
-
- let result = trimStatResults(monthStats, totalMonthsRequired);
- cachedMonthlyUsageData = result;
-
- dataBroadcaster.broadcastMonthlyUsageUpdate(result);
- }
+ });
- });
+ }
setTimeout(fetchMonthlyUsage, 1800000); // 30 mins
}
function fetchPowerState() {
- let deviceId = 1
+ if(app.getWsClientCount() > 0 || cachedPowerState.length === 0) {
- deviceManager.getDevice(deviceId).getSysInfo().then(response => {
+ deviceManager.getAllDevices().forEach(device => {
- let powerState = {
- isOn: (response.relay_state === 1),
- uptime: response.on_time
- };
+ let deviceId = device.deviceId;
+ device.getSysInfo().then(response => {
- cachedPowerState = powerState;
- dataBroadcaster.broadcastPowerStateUpdate(powerState);
- });
+ let powerState = {
+ isOn: (response.relay_state === 1),
+ uptime: response.on_time
+ };
+
+ updateCache(cachedPowerState, deviceId, powerState);
+ dataBroadcaster.broadcastPowerStateUpdate(deviceId, powerState);
+ });
+ });
+
+ }
setTimeout(fetchPowerState, 60000);
}
@@ -218,11 +241,37 @@ function trimStatResults(stats, maxSamples) {
return stats.splice(stats.length - maxSamples, stats.length);
}
-module.exports.getCachedData = function() {
+function getCachedData(cache, deviceId) {
+ let cacheEntry = cache.find(d => d.deviceId == deviceId);
+ if(cacheEntry === undefined) {
+ return cacheEntry;
+ }
+ else {
+ return cacheEntry.data;
+ }
+}
+
+function updateCache(cache, deviceId, data) {
+
+ let cachedData = cache.find(d => d.deviceId == deviceId);
+
+ if(cachedData === undefined) {
+ cache.push({
+ deviceId: deviceId,
+ data: data
+ });
+ }
+ else {
+ cachedData.data = data;
+ }
+}
+
+module.exports.getCachedData = function(deviceId) {
+
return {
- realtimeUsage: cachedRealtimeUsageData,
- dailyUsage: cachedDailyUsageData,
- monthlyUsage: cachedMonthlyUsageData,
- powerState: cachedPowerState
+ realtimeUsage: getCachedData(cachedRealtimeUsageData, deviceId),
+ dailyUsage: getCachedData(cachedDailyUsageData, deviceId),
+ monthlyUsage: getCachedData(cachedMonthlyUsageData, deviceId),
+ powerState: getCachedData(cachedPowerState, deviceId)
}
} \ No newline at end of file
diff --git a/services/device-manager.js b/services/device-manager.js
index 3ed4a26..d1d81a1 100644
--- a/services/device-manager.js
+++ b/services/device-manager.js
@@ -6,11 +6,12 @@ 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];
+
+ return devices.find(d => d.deviceId == deviceId);
+
}
module.exports.getAllDevices = function() {
diff --git a/views/index.hbs b/views/index.hbs
index ef2a514..d0210a7 100644
--- a/views/index.hbs
+++ b/views/index.hbs
@@ -15,8 +15,8 @@
<h3>Devices</h3>
<ul class="nav side-menu">
{{#each devices}}
- <li class="active">
- <a title="Device Id: {{this.deviceId}}">
+ <li id="{{this.deviceId}}">
+ <a title="Device Id: {{this.deviceId}}" href="/{{this.deviceId}}">
<i class="fa fa-plug"></i>
{{this.alias}}
</a>
@@ -242,3 +242,8 @@
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@1.4.0/dist/chartjs-plugin-streaming.min.js"></script>
<script src="/javascripts/dash.js"></script>
+ <script>
+ $(document).ready(function () {
+ dash.init( '{{device.deviceId}}' );
+ });
+ </script>