diff --git a/meshcentral.js b/meshcentral.js index 10c88b2c..eec53f63 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -2203,9 +2203,9 @@ function CreateMeshCentralServer(config, args) { // nodeId: node identifier of format node/domain/nodeidhex // connectTime: time of connection, milliseconds elapsed since the UNIX epoch. // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local, 8 = Intel AMT Relay, 16 = MQTT - // powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present + // powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present, 8 = Off //var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local', '', '', '', 'Intel AMT Relay', '', '', '', '', '', '', '', 'MQTT']; - //var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present']; + //var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present', 'Off']; obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) { //console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid))); if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'SetConnectivityState', meshid: meshid, nodeid: nodeid, connectTime: connectTime, connectType: connectType, powerState: powerState, extraInfo: extraInfo }); } diff --git a/meshipkvm.js b/meshipkvm.js index 518eb94e..bac2bd62 100644 --- a/meshipkvm.js +++ b/meshipkvm.js @@ -37,14 +37,19 @@ function CreateIPKVMManager(parent) { const MESHRIGHT_ADMIN = 0xFFFFFFFF; // Subscribe for mesh creation events - parent.AddEventDispatch(['server-createmesh', 'server-deletemesh'], obj); + parent.AddEventDispatch(['server-createmesh', 'server-deletemesh', 'devport-operation'], obj); obj.HandleEvent = function (source, event, ids, id) { - if ((event != null) && (event.action == 'createmesh') && (event.mtype == 4)) { + if ((event == null) || (event.mtype != 4)) return; + if (event.action == 'createmesh') { // Start managing this new device group startManagement(parent.webserver.meshes[event.meshid]); - } else if ((event != null) && (event.action == 'deletemesh') && (event.mtype == 4)) { + } else if (event.action == 'deletemesh') { // Stop managing this device group stopManagement(event.meshid); + } else if ((event.action == 'turnon') || (event.action == 'turnoff')) { + // Perform power operation + const manager = obj.managedGroups[event.meshid]; + if ((manager) && (manager.powerOperation)) { manager.powerOperation(event); } } } @@ -120,7 +125,7 @@ function CreateIPKVMManager(parent) { const mesh = parent.webserver.meshes[sender.meshid]; if (nodes.length == 0) { // The device does not exist, create it - const device = { type: 'node', mtype: 4, _id: nodeid, icon: 1, meshid: sender.meshid, name: port.Name, rname: port.Name, domain: sender.domainid, portid: port.PortId, portnum: port.PortNumber }; + const device = { type: 'node', mtype: 4, _id: nodeid, icon: 4, meshid: sender.meshid, name: port.Name, rname: port.Name, domain: sender.domainid, portid: port.PortId, portnum: port.PortNumber, porttype: 'PDU' }; parent.db.Set(device); // Event the new node @@ -140,13 +145,13 @@ function CreateIPKVMManager(parent) { // Set the connectivity state if needed if (obj.managedPorts[nodeid] == null) { - parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, port.State?1:6, null, null); + parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, port.State ? 1 : 8, null, null); obj.managedPorts[nodeid] = { name: port.Name, meshid: sender.meshid, portid: port.PortId, portType: port.PortType, portNo: port.PortIndex }; } }); } else { // Update connectivity state - parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, port.State ? 1 : 6, null, null); + parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, port.State ? 1 : 8, null, null); } } else if ((port.Status == 1) && (port.Class == 'KVM')) { //console.log(port.PortNumber + ', ' + port.PortId + ', ' + port.Name + ', ' + port.Type + ', ' + ((port.StatAvailable == 0) ? 'Idle' : 'Connected')); @@ -765,7 +770,7 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) { obj.update(); } - obj.update = function() { + obj.update = function () { obj.fetch('/restapi/relay/outlets/all;/=name,physical_state/', 'GET', null, null, function (sender, tag, rdata, res) { if (res.statusCode == 207) { var rdata2 = null; @@ -780,7 +785,7 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) { var portchanged = false; if (obj.ports[i] == null) { // Add the port - obj.ports[i] = { PortNumber: i, PortId: 'p' + i, Name: portname, Status: 1, State: portstate, Class: 'PDU' }; + obj.ports[i] = { PortNumber: i + 1, PortId: 'p' + i, Name: portname, Status: 1, State: portstate, Class: 'PDU' }; portchanged = true; } else { // Update the port @@ -800,9 +805,15 @@ function CreateWebPowerSwitch(parent, hostname, port, username, password) { }); } + obj.powerOperation = function (event) { + if (typeof event.portnum != 'number') return; + if (event.action == 'turnon') { setPowerState(event.portnum - 1, true); } + else if (event.action == 'turnoff') { setPowerState(event.portnum - 1, false); } + } + function setPowerState(port, state, func) { obj.fetch('/restapi/relay/outlets/' + port + '/state/', 'PUT', 'value=' + state, null, function (sender, tag, rdata, res) { - console.log('DATA:', res.statusCode, rdata.toString()); + if (res.statusCode == 204) { obj.update(); } }); } diff --git a/meshuser.js b/meshuser.js index be1224de..9ba6d6ab 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2889,7 +2889,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Get the node and the rights for this node parent.GetNodeWithRights(domain, user, nodeid, function (node, rights, visible) { - // Check we have the rights to delete this device + // Check we have the rights to wake this device if ((node == null) || (visible == false) || (rights & MESHRIGHT_WAKEDEVICE) == 0) { if (command.nodeids.length == 1) { try { ws.send(JSON.stringify({ action: 'wakedevices', responseid: command.responseid, result: 'Invalid nodeid' })); } catch (ex) { } } return; @@ -2898,6 +2898,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // If this device is connected on MQTT, send a wake action. if (parent.parent.mqttbroker != null) { parent.parent.mqttbroker.publish(node._id, 'powerAction', 'wake'); } + // If this is a IP-KVM or Power Distribution Unit (PDU), dispatch an action event + if (node.mtype == 4) { + // Send out an event to perform turn off command on the port + //var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['devport-operation', 'server-users', user._id]); + var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'turnon', domain: domain.id, nolog: 1, portid: node.portid, porttype: node.porttype, portnum: node.portnum, meshid: node.meshid, mtype: node.mtype }; + parent.parent.DispatchEvent([ 'devport-operation' ], obj, event); + return; + } + // Get the device interface information db.Get('if' + node._id, function (err, nodeifs) { if ((nodeifs != null) && (nodeifs.length == 1)) { @@ -3054,11 +3063,20 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((command.actiontype == 401) && common.validateInt(command.time, 1, 30000)) { routeCommandToNode({ action: 'msg', type: 'console', nodeid: node._id, value: 'vibrate ' + command.time }, MESHRIGHT_ADMIN, 0); } } else { // Check we have the rights to delete this device - if ((rights & MESHRIGHT_RESETOFF) == 0) return; + if ((rights & MESHRIGHT_RESETOFF) == 0) return; // If this device is connected on MQTT, send a power action. if ((parent.parent.mqttbroker != null) && (command.actiontype >= 0) && (command.actiontype <= 4)) { parent.parent.mqttbroker.publish(node._id, 'powerAction', ['', '', 'poweroff', 'reset', 'sleep'][command.actiontype]); } + // If this is a IP-KVM or Power Distribution Unit (PDU), dispatch an action event + if (node.mtype == 4) { + // Send out an event to perform turn off command on the port + //var targets = parent.CreateNodeDispatchTargets(node.meshid, node._id, ['devport-operation', 'server-users', user._id]); + var event = { etype: 'node', userid: user._id, username: user.name, nodeid: node._id, action: 'turnoff', domain: domain.id, nolog: 1, portid: node.portid, porttype: node.porttype, portnum: node.portnum, meshid: node.meshid, mtype: node.mtype }; + parent.parent.DispatchEvent([ 'devport-operation' ], obj, event); + return; + } + if ((command.actiontype >= 300) && (command.actiontype < 400)) { if ((command.actiontype != 302) && (command.actiontype != 308) && (command.actiontype != 310)) return; // Invalid action type. // Intel AMT power command, actiontype: 2 = Power on, 8 = Power down, 10 = reset diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars index 19a1224a..904817c3 100644 --- a/views/default-mobile.handlebars +++ b/views/default-mobile.handlebars @@ -3214,15 +3214,15 @@ return false; } - var powerStatetable = ['', "Powered", "Sleep", "Sleep", "Sleep", "Hibernating", "Power off", "Present"]; - var powerStateStrings = ['', "Powered", "Sleeping", "Sleeping", "Deep Sleep", "Hibernating", "Soft-Off", "Present"]; - var powerStateStrings2 = ['', "Device is powered", "Device is in sleep state (S1)", "Device is in sleep state (S2)", "Device is in deep sleep state (S3)", "Device is hibernating (S4)", "Device is in soft-off state (S5)", "Device is present, but power state cannot be determined"]; + var powerStatetable = ['', "Powered", "Sleep", "Sleep", "Sleep", "Hibernating", "Power off", "Present", "Off"]; + var powerStateStrings = ['', "Powered", "Sleeping", "Sleeping", "Deep Sleep", "Hibernating", "Soft-Off", "Present", "Off"]; + var powerStateStrings2 = ['', "Device is powered", "Device is in sleep state (S1)", "Device is in sleep state (S2)", "Device is in deep sleep state (S3)", "Device is hibernating (S4)", "Device is in soft-off state (S5)", "Device is present, but power state cannot be determined", "The device is powered off"]; var powerColorTable = ['#00000000', 'black', 'blue', 'blue', 'lightblue', 'blueviolet', 'darkgreen', 'lightseagreen', 'lightseagreen']; function NodeStateStr(node) { var states = []; if (node.state > 0 && node.state < powerStatetable.length) state.push(powerStatetable[node.state]); if (node.conn) { - if ((node.conn & 1) != 0) { states.push('' + ((node.mtype == 4)?"IP-KVM":"Agent") + ''); } + if ((node.conn & 1) != 0) { states.push('' + ((node.mtype == 4) ? ((node.porttype == 'PDU') ? "Switch" : "IP-KVM") : "Agent") + ''); } if ((node.conn & 2) != 0) { states.push('' + "CIRA" + ''); } else if ((node.conn & 4) != 0) { states.push('' + "Intel® AMT" + ''); } if ((node.conn & 8) != 0) { states.push('' + "Relay" + ''); } @@ -3382,8 +3382,8 @@ // IP-KVM information if (node.mtype == 4) { - if (node.portnum) { x += addDeviceAttribute("Port Number", node.portnum); } - if (node.porttype) { x += addDeviceAttribute("Port Type", node.porttype); } + if (node.portnum != null) { x += addDeviceAttribute("Port Number", node.portnum); } + if (node.porttype != null) { x += addDeviceAttribute("Port Type", node.porttype); } } // Attribute: Mesh Agent @@ -3486,7 +3486,7 @@ var connectivity = node.conn; if (connectivity && connectivity > 1) { var cstate = []; - if ((node.conn & 1) != 0) cstate.push('' + ((node.mtype == 4) ? "IP-KVM" : "Agent") + ''); + if ((node.conn & 1) != 0) cstate.push('' + ((node.mtype == 4) ? ((node.porttype == 'PDU') ? "Switch" : "IP-KVM") : "Agent") + ''); if ((node.conn & 2) != 0) cstate.push('' + "Intel® AMT CIRA" + ''); else if ((node.conn & 4) != 0) cstate.push('' + "Intel® AMT" + ''); if ((node.conn & 8) != 0) cstate.push('' + "Agent Relay" + ''); @@ -3519,9 +3519,21 @@ x += '
'; // Show action button, only show if we have permissions 4, 8, 64 if (((meshrights & (4 + 8 + 64)) != 0) && (node.mtype < 3)) { x += ''; } - x += ''; + x += ''; //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += ''; } + if ((node.mtype == 4) && (meshrights & 8) && (connectivity & 1)) { + if (node.porttype == 'PDU') { + if (node.pwr == 1) { + x += ''; + } else if (node.pwr == 8) { + x += ''; + } + } else { + x += ''; + } + } + QH('p10html', x); // If we are looking at a local non-windows device, enable terminal and files capability. @@ -3546,11 +3558,11 @@ // Set the node power state var powerstate = PowerStateStr(node.state); //if (node.state == 0) { powerstate = 'Unknown State'; } - if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += ((node.mtype == 4) ? "IP-KVM" : "Mesh Agent"); } + if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += ((node.mtype == 4) ? ((node.porttype == 'PDU') ? "Switch" : "IP-KVM") : "Mesh Agent"); } if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT connected"; } else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT detected"; } if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "MQTT channel connected"; } - if ((node.pwr > 1) && (node.pwr != 7)) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += powerStateStrings[node.pwr]; } + if ((node.porttype == 'PDU') || ((node.pwr > 1) && (node.pwr != 7))) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += powerStateStrings[node.pwr]; } QH('MainComputerState', '' + powerstate + ''); // Set the node icon @@ -3585,6 +3597,20 @@ if (xxcurrentView == 10) { setupDeviceMenu(); } } + function setIpPduState(op) { + if (op == 0) { + setDialogMode(2, "Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: 2 }); }, "Perform power off?"); // Turn off + } else { + setDialogMode(2, "Power Operation", 3, function () { meshserver.send({ action: 'wakedevices', nodeids: [currentNode._id] }); }, "Perform power on?"); // Turn on + } + } + + function openIpKvmRemoteControl(nodeid) { + if (xxdialogMode) return; + var nid = decodeURIComponent(nodeid).split('/')[2]; + safeNewWindow('/ipkvm.ashx/' + nid + '/', 'ipkvm:' + nid); + } + function deviceToastFunction() { if (xxdialogMode) return; setDialogMode(2, "Device Toast", 3, deviceToastFunctionEx, ''); @@ -5956,7 +5982,7 @@ x += addHtmlValue("Username", currentMesh.kvm.user); } - x += '
'; + x += '
'; x += '

'; var currentMeshLinks = currentMesh.links[userinfo._id]; diff --git a/views/default.handlebars b/views/default.handlebars index 3715902d..52ebdfd7 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -4367,10 +4367,16 @@ } else if (view == 2) { var states = []; if (node.conn) { - if (node.mtype == 4) { - if ((node.conn & 1) != 0) { states.push('' + "IP-KVM" + ''); } - } else { - if ((node.conn & 1) != 0) { states.push('' + "Agent" + ''); } + if ((node.conn & 1) != 0) { + if (node.mtype == 4) { + if (node.porttype == 'PDU') { + states.push('' + "Switch" + ''); + } else { + states.push('' + "IP-KVM" + ''); + } + } else { + states.push('' + "Agent" + ''); + } } if ((node.conn & 2) != 0) { states.push('' + "CIRA" + ''); } else if ((node.conn & 4) != 0) { states.push('' + "AMT" + ''); } @@ -5132,8 +5138,8 @@ deviceHeaderTotal = 0; } - var powerStateStrings = ['', '' + "Powered" + '', '' + "Sleeping" + '', '' + "Sleeping" + '', '' + "Deep Sleep" + '', '' + "Hibernating" + '', '' + "Soft-Off" + '', '' + "Present" + '']; - var powerStateStrings2 = ['', "Device is powered", "Device is in sleep state (S1)", "Device is in sleep state (S2)", "Device is in deep sleep state (S3)", "Device is hibernating (S4)", "Device is in soft-off state (S5)", "Device is present, but power state cannot be determined"]; + var powerStateStrings = ['', '' + "Powered" + '', '' + "Sleeping" + '', '' + "Sleeping" + '', '' + "Deep Sleep" + '', '' + "Hibernating" + '', '' + "Soft-Off" + '', '' + "Present" + '', '' + "Off" + '']; + var powerStateStrings2 = ['', "Device is powered", "Device is in sleep state (S1)", "Device is in sleep state (S2)", "Device is in deep sleep state (S3)", "Device is hibernating (S4)", "Device is in soft-off state (S5)", "Device is present, but power state cannot be determined", "The device is powered off"]; var powerColorTable = ['pwsTransparent', 'pwsBlack', 'pwsBlue', 'pwsBlue2', 'pwsLightblue', 'pwsBlueviolet', 'pwsDarkgreen', 'pwsLightseagreen', 'pwsLightseagreen2']; function NodeStateStr(node) { var states = []; @@ -5141,7 +5147,11 @@ if (node.conn) { if ((node.conn & 1) != 0) { if (node.mtype == 4) { - states.push('' + "IP-KVM" + ''); + if (node.porttype == 'PDU') { + states.push('' + "Switch" + ''); + } else { + states.push('' + "IP-KVM" + ''); + } } else { states.push('' + "Agent" + ''); } @@ -6686,10 +6696,10 @@ x += addDeviceAttribute("Description", description); } - // IP-KVM information + // IP-KVM / PDU information if (node.mtype == 4) { - if (node.portnum) { x += addDeviceAttribute("Port Number", node.portnum); } - if (node.porttype) { x += addDeviceAttribute("Port Type", node.porttype); } + if (node.portnum != null) { x += addDeviceAttribute("Port Number", node.portnum); } + if (node.porttype != null) { x += addDeviceAttribute("Port Type", node.porttype); } } // Attribute: Mesh Agent @@ -6893,7 +6903,15 @@ } } if ((node.mtype == 4) && (meshrights & 8) && (connectivity & 1)) { - x += ''; + if (node.porttype == 'PDU') { + if (node.pwr == 1) { + x += ''; + } else if (node.pwr == 8) { + x += ''; + } + } else { + x += ''; + } } // Custom UI @@ -7007,7 +7025,11 @@ if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += '
'; } if (node.mtype == 4) { - powerstate += '' + "IP-KVM port connected" + '' + agentPrivilages; + if (node.porttype == 'PDU') { + powerstate += '' + "Switch port connected" + '' + agentPrivilages; + } else { + powerstate += '' + "IP-KVM port connected" + '' + agentPrivilages; + } } else { powerstate += '' + "Agent connected" + '' + agentPrivilages; } @@ -7016,7 +7038,10 @@ else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += '
'; } powerstate += '' + "Intel® AMT detected" + ''; } if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += '
'; } powerstate += '' + "MQTT channel connected" + ''; } if ((powerstate == '') && node.lastconnect) { powerstate = '' + "Last seen:" + '
' + printDateTime(new Date(node.lastconnect)) + '
'; } - else { if ((node.pwr > 1) && (node.pwr != 7)) { powerstate += ('
' + powerStateStrings[node.pwr]); } } + else { + if (node.porttype == 'PDU') { powerstate += ('
' + powerStateStrings[node.pwr]); } + else if ((node.pwr > 1) && (node.pwr != 7)) { powerstate += ('
' + powerStateStrings[node.pwr]); } + } QH('MainComputerState', powerstate); // Set the node icon @@ -7193,6 +7218,14 @@ go(panel); } + function setIpPduState(op) { + if (op == 0) { + setDialogMode(2, "Power Operation", 3, function() { meshserver.send({ action: 'poweraction', nodeids: [ currentNode._id ], actiontype: 2 }); }, "Perform power off?"); // Turn off + } else { + setDialogMode(2, "Power Operation", 3, function() { meshserver.send({ action: 'wakedevices', nodeids: [ currentNode._id ] }); }, "Perform power on?"); // Turn on + } + } + function p20editDeviceNotify() { if (xxdialogMode) return false; var devNotify = 0, fx = ((features2 & 0x00004000) && (userinfo.emailVerified))?1:0;