diff options
| author | James Barnett <james.barnett@fivium.co.uk> | 2018-04-08 17:25:47 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-04-08 17:25:47 +0100 |
| commit | eb0185c03f8d5b68949798e8040b42a203b040a2 (patch) | |
| tree | 86da8449b0c92ec29a15baffe3e7bd8ebd34488e /services/data-fetcher.js | |
| parent | 954aff49707738e660e92e0418c31b1ec78a85a8 (diff) | |
| parent | 4ec37af74fc77f7381d7c5c3b2560c8726f75ffa (diff) | |
| download | tplink-energy-monitor-eb0185c03f8d5b68949798e8040b42a203b040a2.tar.xz tplink-energy-monitor-eb0185c03f8d5b68949798e8040b42a203b040a2.zip | |
Merge pull request #1 from jamesbarnett91/websocketsv0.2
Switch from http polling to websockets
Diffstat (limited to 'services/data-fetcher.js')
| -rw-r--r-- | services/data-fetcher.js | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/services/data-fetcher.js b/services/data-fetcher.js new file mode 100644 index 0000000..7ef3594 --- /dev/null +++ b/services/data-fetcher.js @@ -0,0 +1,277 @@ +const deviceManager = require('./device-manager'); +const dataBroadcaster = require('./data-broadcaster'); +const app = require('../app'); +const moment = require('moment'); + +// Get initial data after a short delay to allow the device manager to find devices +// TODO run once device manager notifies its complete instead +setTimeout(function() { + fetchRealtimeUsage(); + fetchDailyUsage(); + fetchMonthlyUsage(); + fetchPowerState(); +}, 2000) + +let cachedRealtimeUsageData = []; +let cachedDailyUsageData = []; +let cachedMonthlyUsageData = []; +let cachedPowerState = []; + +function fetchRealtimeUsage() { + + if(app.getWsClientCount() > 0 || cachedRealtimeUsageData.length === 0) { + + deviceManager.getAllDevices().forEach(device => { + + let deviceId = device.deviceId; + device.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); + + updateCache(cachedRealtimeUsageData, deviceId, response); + + dataBroadcaster.broadcastRealtimeUsageUpdate(deviceId, response); + }); + + }); + + } + + setTimeout(fetchRealtimeUsage, 1000); +} + +function fetchDailyUsage() { + + 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); + + let result = trimStatResults(dayStats, totalDaysRequired); + updateCache(cachedDailyUsageData, deviceId, result); + + dataBroadcaster.broadcastDailyUsageUpdate(deviceId, result); + } + + }); + + }); + + } + + setTimeout(fetchDailyUsage, 300000); // 5 mins; +} + +function fetchMonthlyUsage() { + + 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); + } + + }); + + }); + + } + + setTimeout(fetchMonthlyUsage, 1800000); // 30 mins +} + +function fetchPowerState() { + + if(app.getWsClientCount() > 0 || cachedPowerState.length === 0) { + + deviceManager.getAllDevices().forEach(device => { + + let deviceId = device.deviceId; + device.getSysInfo().then(response => { + + let powerState = { + isOn: (response.relay_state === 1), + uptime: response.on_time + }; + + updateCache(cachedPowerState, deviceId, powerState); + + dataBroadcaster.broadcastPowerStateUpdate(deviceId, powerState); + }); + }); + + } + setTimeout(fetchPowerState, 60000); +} + + +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); +} + +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: getCachedData(cachedRealtimeUsageData, deviceId), + dailyUsage: getCachedData(cachedDailyUsageData, deviceId), + monthlyUsage: getCachedData(cachedMonthlyUsageData, deviceId), + powerState: getCachedData(cachedPowerState, deviceId) + } +}
\ No newline at end of file |