diff --git a/meshuser.js b/meshuser.js index 3be6aa8a..f04ba413 100644 --- a/meshuser.js +++ b/meshuser.js @@ -342,33 +342,30 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use var timeline = [], time = null, previousPower; for (i in docs) { var doc = docs[i]; + + // Skip all starting power 0 events. + if ((time == null) && (doc.power == 0) && ((doc.oldPower == null) || (doc.oldPower == 0))) { continue; } + doc.time = Date.parse(doc.time); if (time == null) { // First element time = doc.time; if (doc.oldPower) { timeline.push(doc.oldPower); } else { timeline.push(0); } - timeline.push(time); + timeline.push(time / 1000); timeline.push(doc.power); previousPower = doc.power; } else { // Delta element - if ((previousPower != doc.power) && ((doc.time - time) > 60000)) { // To boost speed, any blocks less than a minute get approximated. + if ((previousPower != doc.power) && ((doc.time - time) > 60000)) { // To boost speed, small blocks get approximated. // Create a new timeline - timeline.push(doc.time - time); + timeline.push((doc.time - time) / 1000); timeline.push(doc.power); time = doc.time; previousPower = doc.power; } else { - // Extend the previous timeline - if ((timeline.length >= 6) && (timeline[timeline.length - 3] == doc.power)) { // We can merge the block with the previous block - timeline[timeline.length - 4] += (timeline[timeline.length - 2] + (doc.time - time)); - timeline.pop(); - timeline.pop(); - } else { // Extend the last block in the timeline - timeline[timeline.length - 2] += (doc.time - time); - timeline[timeline.length - 1] = doc.power; - } - time = doc.time; + // Merge with previous timeline + timeline[timeline.length - 2] += ((doc.time - time) / 1000); + timeline[timeline.length - 1] = doc.power; previousPower = doc.power; } } diff --git a/package.json b/package.json index 95f1a61f..7c655902 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.3.0-r", + "version": "0.3.0-s", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default.handlebars b/views/default.handlebars index 5d69d77d..547e3738 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1279,6 +1279,7 @@ powerTimelineNode = message.nodeid; powerTimeline = message.timeline; powerTimelineUpdate = Date.now() + 300000; // Update every 5 minutes + for (var i in powerTimeline) { if (i % 2 == 1) { powerTimeline[i] = powerTimeline[i] * 1000; } } // Decompress time if (currentNode._id == message.nodeid) { masterUpdate(256); } break; } @@ -3867,7 +3868,13 @@ ++count; date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day } - QH('p10html2', '' + x + '
Day7 Day Power State
'); + QH('p10html2', '' + x + '
Day7 Day Power State
'); + } + + // Download the power events for the current device + function p10downloadPowerEvents() { + console.log("devicepowerevents?id=" + currentNode._id); + fileDownload("devicepowerevents.ashx?id=" + currentNode._id, "powerevents-" + currentNode.name + ".csv"); } // Return a color for the given power state diff --git a/webserver.js b/webserver.js index d8740bcc..05f2710c 100644 --- a/webserver.js +++ b/webserver.js @@ -2284,6 +2284,46 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { res.send(meshsettings); }; + // Handle a request for power events + obj.handleDevicePowerEvents = function (req, res) { + const domain = checkUserIpAddress(req, res); + if (domain == null) return; + if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (req.query.id == null) || (typeof req.query.id != 'string')) { res.sendStatus(401); return; } + var x = req.query.id.split('/'); + var user = obj.users[req.session.userid]; + if ((x.length != 3) || (x[0] != 'node') || (x[1] != domain.id) || (user == null) || (user.links == null)) { res.sendStatus(401); return; } + + obj.db.Get(req.query.id, function (err, docs) { + if (docs.length != 1) { + res.sendStatus(401); + } else { + var node = docs[0]; + + // Check if we have right to this node + var rights = 0; + for (var i in user.links) { if (i == node.meshid) { rights = user.links[i].rights; } } + if (rights == 0) { res.sendStatus(401); return; } + + // Get the list of power events and send them + res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/csv', 'Content-Disposition': 'attachment; filename=powerevents.csv' }); + obj.db.getPowerTimeline(node._id, function (err, docs) { + var xevents = [ 'Time, State, Previous State' ], prevState = 0; + for (var i in docs) { + if (docs[i].power != prevState) { + prevState = docs[i].power; + if (docs[i].oldPower != null) { + xevents.push(docs[i].time.toString() + ',' + docs[i].power + ',' + docs[i].oldPower); + } else { + xevents.push(docs[i].time.toString() + ',' + docs[i].power); + } + } + } + res.send(xevents.join('\r\n')); + }); + } + }); + } + // Starts the HTTPS server, this should be called after the user/mesh tables are loaded function serverStart() { // Start the server, only after users and meshes are loaded from the database. @@ -2368,6 +2408,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.app.get(url + 'messenger', handleMessengerRequest); obj.app.get(url + 'meshosxagent', obj.handleMeshOsxAgentRequest); obj.app.get(url + 'meshsettings', obj.handleMeshSettingsRequest); + obj.app.get(url + 'devicepowerevents.ashx', obj.handleDevicePowerEvents); obj.app.get(url + 'downloadfile.ashx', handleDownloadFile); obj.app.post(url + 'uploadfile.ashx', handleUploadFile); obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile);