├── examples ├── config.js ├── set-outlet-state.js └── outlet-info.js ├── .gitignore ├── package.json ├── LICENSE ├── lib └── app.js └── README.md /examples/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | host: '10.0.20.40' 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | .DS_Store 4 | ._* 5 | .idea 6 | -------------------------------------------------------------------------------- /examples/set-outlet-state.js: -------------------------------------------------------------------------------- 1 | var apcPdu = require('../lib/app') 2 | , config = require('./config'); 3 | 4 | var pdu = new apcPdu(config); 5 | 6 | /** 7 | * Define the port to turn on 8 | */ 9 | var port = 14; 10 | 11 | /** 12 | * Turn on the port specified above 13 | */ 14 | pdu.setPowerState(port, true, function(err) { 15 | if(err) { 16 | console.log(err.toString()); 17 | return; 18 | } 19 | 20 | console.log('Successfully turned port', port, 'on'); 21 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apc-pdu-snmp", 3 | "version": "1.2.0", 4 | "description": "APC PDU module utilizing SNMP", 5 | "main": "./lib/app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/phillipsnick/apc-pdu-snmp.git" 12 | }, 13 | "keywords": [ 14 | "apc", 15 | "pdu", 16 | "snmp" 17 | ], 18 | "author": "Nick Phillips ", 19 | "license": "MIT", 20 | "dependencies": { 21 | "net-snmp": "~1.1.13" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nick Phillips 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/outlet-info.js: -------------------------------------------------------------------------------- 1 | var apcPdu = require('../lib/app') 2 | , config = require('./config'); 3 | 4 | var pdu = new apcPdu(config); 5 | 6 | /** 7 | * Get the total number of outlets 8 | */ 9 | pdu.getTotalOutlets(function(err, count) { 10 | if (err) { 11 | console.log(err.toString()); 12 | return; 13 | } 14 | 15 | console.log('Total outlets:', count); 16 | }); 17 | 18 | /** 19 | * Get the name for outlet 1 20 | */ 21 | pdu.getOutletName(1, function(err, name) { 22 | if (err) { 23 | console.log(err.toString()); 24 | return; 25 | } 26 | 27 | console.log('Outlet name for port 1 is:', name); 28 | }); 29 | 30 | /** 31 | * Get the names of all the outlets 32 | */ 33 | pdu.getAllOutletNames(function(err, names) { 34 | if (err) { 35 | console.log(err.toString()); 36 | return; 37 | } 38 | 39 | console.log('All outlet names:', names); 40 | }); 41 | 42 | /** 43 | * Get the power state of a specific outlet 44 | */ 45 | pdu.getOutletPowerState(1, function(err, state) { 46 | if (err) { 47 | console.log(err.toString()); 48 | return; 49 | } 50 | 51 | console.log('Outlet 1 is currently:', state == '1' ? 'On' : 'Off'); 52 | }); 53 | 54 | /** 55 | * Get the total PDU power draw 56 | */ 57 | pdu.getPowerDraw(function(err, draw) { 58 | if (err) { 59 | console.log(err.toString()); 60 | return; 61 | } 62 | 63 | console.log('Power draw is currently:', draw, 'amps'); 64 | }); 65 | 66 | /** 67 | * Get the low load warning threshold 68 | */ 69 | pdu.getLowLoadThreshold(function(err, amps) { 70 | if (err) { 71 | console.log(err.toString()); 72 | return; 73 | } 74 | 75 | console.log('Low warning threshold is', amps, 'amps'); 76 | }); 77 | 78 | /** 79 | * Get the near load warning threshold 80 | */ 81 | pdu.getNearLoadThreshold(function(err, amps) { 82 | if (err) { 83 | console.log(err.toString()); 84 | return; 85 | } 86 | 87 | console.log('Near overload warning threshold is,', amps, 'amps'); 88 | }); 89 | 90 | /** 91 | * Get the overload threshold (amps) 92 | */ 93 | pdu.getOverloadThreshold(function(err, amps) { 94 | if (err) { 95 | console.log(err.toString()); 96 | return; 97 | } 98 | 99 | console.log('Overload alarm threshold is', amps, 'amps'); 100 | }); 101 | 102 | /** 103 | * Get the load state 104 | */ 105 | pdu.getLoadState(function(err, state) { 106 | if (err) { 107 | console.log(err.toString()); 108 | return; 109 | } 110 | 111 | console.log('The current load state is', apcPdu.loadState[state], '(', state, ')'); 112 | }); -------------------------------------------------------------------------------- /lib/app.js: -------------------------------------------------------------------------------- 1 | var snmp = require('net-snmp'); 2 | 3 | /** 4 | * Create the SNMP session 5 | * 6 | * @param options 7 | */ 8 | function pdu(options) { 9 | this.options = options; 10 | 11 | if (typeof options.host === "undefined") { 12 | throw "Please define a host"; 13 | } 14 | community = this.options.community || 'private' 15 | this.session = snmp.createSession(this.options.host, community, { 16 | version: snmp.Version1, 17 | timeout: 1000 18 | }); 19 | 20 | // beginning of all oid's 21 | this.oid = "1.3.6.1.4.1.318."; 22 | } 23 | 24 | module.exports = pdu; 25 | 26 | /** 27 | * Possible load state translations 28 | */ 29 | pdu.loadState = { 30 | 1: 'bankLoadNormal', 31 | 2: 'bankLoadLow', 32 | 3: 'bankLoadNearOverload', 33 | 4: 'bankLoadOverload' 34 | } 35 | 36 | /** 37 | * Get single parameter by oid 38 | * 39 | * @param string oid 40 | * @param function callback 41 | * @param string errorStr 42 | */ 43 | pdu.prototype.getByOid = function(oid, callback, errStr) { 44 | oid = this.oid + oid; 45 | 46 | this.session.get([oid], function(err, varbinds) { 47 | if (err) { 48 | callback(err); 49 | return; 50 | } 51 | 52 | if (typeof varbinds[0] === "undefined") { 53 | if (typeof errStr === "undefined") { 54 | errStr = new Error('Invalid response from SNMP'); 55 | } 56 | 57 | callback(errStr); 58 | return; 59 | } 60 | 61 | callback(null, varbinds[0].value.toString()); 62 | }); 63 | } 64 | 65 | /** 66 | * Get the total number of outlets 67 | * 68 | * @param function callback 69 | */ 70 | pdu.prototype.getTotalOutlets = function(callback) { 71 | // Using PowerNet-MIB::rPDUIdentDeviceNumOutlets.0 72 | this.getByOid('1.1.12.1.8.0', callback, 'Unable to get total number of outlets'); 73 | } 74 | 75 | /** 76 | * Get a single outlet name 77 | * 78 | * @param int outlet - Outlet port number (1-24) 79 | * @param function callback 80 | */ 81 | pdu.prototype.getOutletName = function(outlet, callback) { 82 | // Using PowerNet-MIB::sPDUOutletCtlName. 83 | this.getByOid('1.1.4.4.2.1.4.' + outlet, callback, 'Unable to get the outlet name'); 84 | } 85 | 86 | /** 87 | * Get all outlet names 88 | * 89 | * @param function callback 90 | */ 91 | pdu.prototype.getAllOutletNames = function(callback) { 92 | // Subtree for PowerNet-MIB::sPDUOutletCtl. 93 | var oid = this.oid + '1.1.4.4.2.1.4'; 94 | var names = {}; 95 | 96 | this.session.subtree(oid, 30, function(varbinds) { 97 | var parts = varbinds[0].oid.split('.'); 98 | var outletNum = parts[parts.length - 1]; 99 | 100 | names[outletNum] = varbinds[0].value.toString(); 101 | }, function(err) { 102 | if (err) { 103 | callback(err); 104 | return; 105 | } 106 | 107 | callback(null, names); 108 | }); 109 | } 110 | 111 | /** 112 | * Get the current power state of an outlet 113 | * 114 | * @param int outlet 115 | * @param function callback 116 | */ 117 | pdu.prototype.getOutletPowerState = function(outlet, callback) { 118 | // Using PowerNet-MIB::sPDUOutletCtl. 119 | this.getByOid('1.1.4.4.2.1.3.' + outlet, callback, 'Unable to get outlet power state'); 120 | } 121 | 122 | /** 123 | * Get the total power draw of the unit 124 | * 125 | * @param function callback 126 | */ 127 | pdu.prototype.getPowerDraw = function(callback) { 128 | // Using PowerNet-MIB::rPDULoadStatusLoad.1 129 | var oid = this.oid + '1.1.12.2.3.1.1.2.1'; 130 | 131 | this.session.get([oid], function(error, varbinds) { 132 | if(error) { 133 | return callback(error); 134 | } 135 | 136 | if (varbinds.length !== 1) { 137 | callback(new Error('Unable to get power draw')); 138 | return; 139 | } 140 | 141 | callback(null, varbinds[0].value / 10); 142 | }); 143 | } 144 | 145 | /** 146 | * Set the power state (on/off) of an outlet 147 | * 148 | * @param int outlet 149 | * @param bool/int state 150 | * @param function callback 151 | */ 152 | pdu.prototype.setPowerState = function(outlet, state, callback) { 153 | var oid = this.oid + '1.1.4.4.2.1.3.' + outlet; 154 | switch (typeof state) { 155 | case 'boolean': var value = state === true ? 1 : 2 156 | case 'number': var value = state 157 | default: callback(new Error('State must be boolean or number')); 158 | } 159 | 160 | this.session.set([{ 161 | oid: oid, 162 | type: snmp.ObjectType.Integer, 163 | value: value 164 | }], function(error, varbinds) { 165 | if (error) { 166 | return callback(error); 167 | } 168 | 169 | if (varbinds.length !== 1) { 170 | callback(new Error('Unable to set the power state for this outlet')); 171 | return; 172 | } 173 | 174 | if (varbinds[0].value !== value) { 175 | callback(new Error('Unable to change the power status for this outlet')); 176 | return; 177 | } 178 | 179 | callback(); 180 | }); 181 | } 182 | 183 | /** 184 | * Get the low load warning threshold 185 | * 186 | * @param function callback 187 | */ 188 | pdu.prototype.getLowLoadThreshold = function(callback) { 189 | // Using PowerNet-MIB::rPDULoadPhaseConfigLowLoadThreshold.phase1 190 | this.getByOid('1.1.12.2.2.1.1.2.1', callback, 'Unable to get the low warning threshold'); 191 | } 192 | 193 | /** 194 | * Get the near load warning threshold 195 | * 196 | * @param function callback 197 | */ 198 | pdu.prototype.getNearLoadThreshold = function(callback) { 199 | // Using PowerNet-MIB::rPDULoadPhaseConfigNearOverloadThreshold.phase1 200 | this.getByOid('1.1.12.2.2.1.1.3.1', callback, 'Unable to get the near warning threshold') 201 | } 202 | 203 | /** 204 | * Get the over load alarm threshold 205 | * 206 | * @param function callback 207 | */ 208 | pdu.prototype.getOverloadThreshold = function(callback) { 209 | // Using PowerNet-MIB::rPDULoadPhaseConfigOverloadThreshold.phase1 210 | this.getByOid('1.1.12.2.2.1.1.4.1', callback, 'Unable to get overload threshold'); 211 | } 212 | 213 | /** 214 | * Get the load state 215 | * bankLoadNormal(1) 216 | * bankLoadLow(2) 217 | * bankLoadNearOverload(3) 218 | * bankLoadOverload(4) 219 | * 220 | * @param function callback 221 | */ 222 | pdu.prototype.getLoadState = function(callback) { 223 | // Using PowerNet-MIB::rPDULoadStatusLoadState.0 224 | this.getByOid('1.1.12.2.3.1.1.3.0', callback, 'Unable to get load state'); 225 | } 226 | 227 | /** 228 | * Get the SNMP session as created by net-snmp library 229 | */ 230 | pdu.prototype.getSnmpSession = function() { 231 | return this.session; 232 | } 233 | 234 | /** 235 | * Close the SNMP session as provided by snmp library 236 | */ 237 | pdu.prototype.close = function() { 238 | this.session.close(); 239 | } 240 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APC PDU SNMP Control 2 | 3 | Module for providing basic management of APC PDUs. Such as turning outlets on and off, the current power state and total power draw for the PDU. 4 | 5 | 6 | ## Installation 7 | 8 | ``` 9 | npm install apc-pdu-snmp 10 | ``` 11 | 12 | 13 | ## Usage 14 | 15 | 16 | Currently the only option which is supported is `host` when creating the object. 17 | 18 | ```javascript 19 | var apcPdu = require('apc-pdu-snmp'); 20 | 21 | var pdu = new apcPdu({ 22 | host: '', // IP Address/Hostname 23 | community: 'private' // Optional community 24 | }); 25 | ``` 26 | 27 | A full range of examples can be found within the [examples directory](https://github.com/phillipsnick/apc-pdu-snmp/tree/master/examples). 28 | 29 | 30 | ### Method 31 | 32 | ### getTotalOutlets(callback) 33 | 34 | Get the total number of outlets on the PDU. 35 | 36 | __Arguments__ 37 | 38 | * `callback(err, totalOutlets)` - Callback function for error/response handling 39 | 40 | __Example__ 41 | 42 | ```js 43 | pdu.getTotalOutlets(function(err, totalOutlets) { 44 | if (err) { 45 | console.log(err); 46 | return; 47 | } 48 | 49 | console.log('Total outlets:', totalOutlets); 50 | }); 51 | ``` 52 | 53 | --------------------------------------- 54 | 55 | ### getOutletName(outletNumber, callback) 56 | 57 | Get an outlet name as configured on the PDU. 58 | 59 | __Arguments__ 60 | 61 | * `outletNumber` - Integer of the outlet number to get the name for 62 | * `callback(err, name)` - Callback function for error/response handling 63 | 64 | __Example__ 65 | 66 | Get the name out outlet 1. 67 | 68 | ```js 69 | pdu.getOutletName(1, function(err, name) { 70 | if (err) { 71 | console.log(err); 72 | return; 73 | } 74 | 75 | console.log('Outlet 1 name:', name); 76 | }); 77 | ``` 78 | 79 | --------------------------------------- 80 | 81 | ### getOutletNames(callback) 82 | 83 | Get all outlet names as configured on the PDU. 84 | 85 | __Arguments__ 86 | 87 | * `callback(err, names)` - Callback function for error/response handling 88 | 89 | __Example__ 90 | 91 | ```js 92 | pdu.getOutletNames(function(err, names) { 93 | if (err) { 94 | console.log(err); 95 | return; 96 | } 97 | 98 | console.log('All outlet names:', names); 99 | }); 100 | ``` 101 | 102 | The names variable will contain an object, keys as the outlet number and the value being the outlet name eg. 103 | 104 | ``` 105 | { 106 | '1': 'Outlet 1', 107 | '2': 'Outlet 2', 108 | '3': 'Outlet 3' 109 | } 110 | ``` 111 | 112 | --------------------------------------- 113 | 114 | ### getOutletPowerState(outletNumber, callback) 115 | 116 | Get the power state of an outlet. 117 | 118 | State variable will return either: 119 | 120 | * 0 - off 121 | * 1 - on 122 | 123 | __Arguments__ 124 | 125 | * `outletNumber` - Integer of the outlet number to get the power state for 126 | * `callback(err, state)` - Callback function for error/response handling 127 | 128 | __Example__ 129 | 130 | Get the power state for outlet 1. 131 | 132 | ```js 133 | pdu.getOutletPowerState(1, function(err, state) { 134 | if (err) { 135 | console.log(err); 136 | return; 137 | } 138 | 139 | console.log('Outlet 1 is currently:', state == '1' ? 'On' : 'Off'); 140 | }); 141 | ``` 142 | 143 | --------------------------------------- 144 | 145 | ### getPowerDraw(callback) 146 | 147 | Get the power draw of the whole PDU in amps. 148 | 149 | __Arguments__ 150 | 151 | * `callback(err, amps)` - Callback function for error/response handling 152 | 153 | __Example__ 154 | 155 | ```js 156 | pdu.getPowerDraw(function(err, amps) { 157 | if (err) { 158 | console.log(err); 159 | return; 160 | } 161 | 162 | console.log('Power draw is currently:', draw, 'amps'); 163 | }); 164 | ``` 165 | 166 | --------------------------------------- 167 | 168 | ### getLowLoadThreshold(callback) 169 | 170 | Get the configured low load warning threshold in amps. 171 | 172 | __Arguments__ 173 | 174 | * `callback(err, amps)` - Callback function for error/response handling 175 | 176 | __Example__ 177 | 178 | ```js 179 | pdu.getLowLoadThreshold(function(err, amps) { 180 | if (err) { 181 | console.log(err); 182 | return; 183 | } 184 | 185 | console.log('Low warning threshold is', amps, 'amps'); 186 | }); 187 | ``` 188 | 189 | --------------------------------------- 190 | 191 | ### getNearLoadThreshold(callback) 192 | 193 | Get the configured near load warning threshold in amps. 194 | 195 | __Arguments__ 196 | 197 | * `callback(err, amps)` - Callback function for error/response handling 198 | 199 | __Example__ 200 | 201 | ```js 202 | pdu.getNearLoadThreshold(function(err, amps) { 203 | if (err) { 204 | console.log(err); 205 | return; 206 | } 207 | 208 | console.log('Near overload warning threshold is,', amps, 'amps'); 209 | }); 210 | ``` 211 | 212 | --------------------------------------- 213 | 214 | ### getOverloadThreshold(callback) 215 | 216 | Get the configured overload alarm threshold in amps. 217 | 218 | __Arguments__ 219 | 220 | * `callback(err, amps)` - Callback function for error/response handling 221 | 222 | __Example__ 223 | 224 | ```js 225 | pdu.getOverloadThreshold(function(err, amps) { 226 | if (err) { 227 | console.log(err); 228 | return; 229 | } 230 | 231 | console.log('Overload alarm threshold is', amps, 'amps'); 232 | }); 233 | ``` 234 | 235 | --------------------------------------- 236 | 237 | ### getLoadState(callback) 238 | 239 | Get the current load state. 240 | 241 | __Arguments__ 242 | 243 | * `callback(err, state)` - Callback function for error/response handling. The `state` variable will contain an integer as per the table below. 244 | 245 | |State|Translation| 246 | |-----|-----------| 247 | |1|bankLoadNormal| 248 | |2|bankLoadLow| 249 | |3|bankLoadNearOverload| 250 | |4|bankLoadOverload| 251 | 252 | The translated key is available within the library using `apcPdu.loadState`, as shown in the example below. 253 | 254 | __Example__ 255 | 256 | ```js 257 | pdu.getLoadState(function(err, state) { 258 | if (err) { 259 | console.log(err); 260 | return; 261 | } 262 | 263 | console.log('The current load state is', apcPdu.loadState[state], '(', state, ')'); 264 | }); 265 | ``` 266 | 267 | --------------------------------------- 268 | 269 | ### setPowerState(outletNumber, state, callback) 270 | 271 | Turn an outlet on/off. 272 | 273 | __Arguments__ 274 | 275 | * `outletNumber` - Outlet as an integer 276 | * `state` - Boolean, true is on, false is off. Alternatively a number, 3 is reboot. 277 | * `callback(err)` - Callback for error/success handling 278 | 279 | __Example__ 280 | 281 | Turn outlet 1 on. 282 | 283 | ```js 284 | pdu.setPowerState(1, true, function(err) { 285 | if (err) { 286 | console.log(err); 287 | return; 288 | } 289 | 290 | console.log('Successfully turned outlet 1 on'); 291 | }); 292 | ``` 293 | 294 | The state variable should be a boolean or number. If a boolean, use true to turn an outlet on and false to turn and outlet off. If a number, use a raw sPDUOutletCtl code: 295 | 296 | * outletOn: 1 297 | * outletOff: 2 298 | * outletReboot: 3 299 | * outletUnknown: 4 300 | * outletOnWithDelay: 5 301 | * outletOffWithDelay: 6 302 | * outletRebootWithDelay: 7 303 | 304 | 305 | 306 | ## Notes 307 | 308 | Hopefully will have some time in the future to improve the features and add some tests as using the examples for testing is pretty bad :) 309 | 310 | All the MIBs have been hard coded into this module, for more details see the PowerNet-MIB ftp://ftp.apc.com/apc/public/software/pnetmib/mib/411/powernet411.mib 311 | 312 | 313 | ## Acknowledgements 314 | 315 | Wouldn't have been able to build this without this great article on SNMP for APC PDUs by Joshua Tobin [SNMP Tutorial – APC PDUs](http://tobinsramblings.wordpress.com/2011/05/03/snmp-tutorial-apc-pdus/) 316 | 317 | 318 | ## Contributors 319 | 320 | * [@createp](https://github.com/cretep) 321 | 322 | ## Licence 323 | 324 | [The MIT License (MIT)](https://github.com/phillipsnick/apc-pdu-snmp/blob/master/LICENSE) 325 | --------------------------------------------------------------------------------