UNPKG

homebridge-platform-snmp

Version:

An SNMP platform plugin for Homebridge: https://github.com/nfarina/homebridge

1,092 lines (873 loc) 36.7 kB
/* jshint asi: true, esversion: 6, node: true, laxbreak: true, laxcomma: true, undef: true, unused: true */ const NodeCache = require('node-cache') , arp = require('arp-a') , debug = require('debug')('homebridge-platform-snmp') , discovery = require('homespun-discovery').observers.snmp , snmpn = require('snmp-native') , underscore = require('underscore') , util = require('util') let Accessory , Service , Characteristic , CommunityTypes , UUIDGen module.exports = function (homebridge) { Accessory = homebridge.platformAccessory Service = homebridge.hap.Service Characteristic = homebridge.hap.Characteristic CommunityTypes = require('hap-nodejs-community-types')(homebridge) UUIDGen = homebridge.hap.uuid homebridge.registerPlatform('homebridge-platform-snmp', 'SNMP', SNMP, true) util.inherits(PowerService, Service) } const PowerService = function (displayName, subtype) { Service.call(this, displayName, '00000001-0000-1000-8000-135D67EC4377', subtype) this.addCharacteristic(CommunityTypes.Volts) this.addCharacteristic(CommunityTypes.VoltAmperes) this.addCharacteristic(CommunityTypes.Watts) this.addCharacteristic(CommunityTypes.KilowattHours) this.addCharacteristic(CommunityTypes.Amperes) this.addOptionalCharacteristic(Characteristic.Name) } // https://github.com/homespun/homebridge-accessory-neurio/blob/master/index.js#L143 PowerService.UUID = '00000001-0000-1000-8000-135D67EC4377' const SNMP = function (log, config, api) { if (!(this instanceof SNMP)) return new SNMP(log, config, api) this.log = log this.config = config || { platform: 'SNMP' } this.api = api this.options = underscore.defaults(this.config.options || {}, { verboseP: false }) this.agents = {} this.discoveries = {} discovery.init() if (api) this.api.on('didFinishLaunching', this._didFinishLaunching.bind(this)) else this._didFinishLaunching() } SNMP.prototype._didFinishLaunching = function () { const self = this self.observer = new discovery.Observe({ sysObjectIDs: underscore.keys(sysObjectIDs) }) self.observer.on('error', function (err) { self.log.error('discovery', err) }).on('up', function (options, service) { const agentId = service.host + ':' + service.port , sysObjectID = service.packet && service.packet.pdu && service.packet.pdu.varbinds && service.packet.pdu.varbinds[1] && service.packet.pdu.varbinds[1].value if (!self.agents[agentId]) { if (!sysObjectIDs[sysObjectID]) return self.log.error('unknown sysObjectID: ' + sysObjectID) self.agents[agentId] = { agent: new (sysObjectIDs[sysObjectID])(self, agentId, service) } } self.agents[agentId].timestamp = underscore.now() }) setTimeout(function () { underscore.keys(self.discoveries).forEach(function (uuid) { const accessory = self.discoveries[uuid] self.log.warn('accessory not (yet) discovered', { UUID: uuid }) accessory.updateReachability(false) }) }.bind(self), 5 * 1000) self.log('didFinishLaunching') } SNMP.prototype._addAccessory = function (agent) { const self = this const accessory = new Accessory(agent.name, agent.uuid) accessory.on('identify', function (paired, callback) { self.log(accessory.displayName, ': identify request') callback() }) agent.attachAccessory.bind(agent)(accessory) if (!self.discoveries[accessory.UUID]) { self.api.registerPlatformAccessories('homebridge-platform-snmp', 'SNMP', [ accessory ]) self.log('addAccessory', underscore.pick(self, [ 'uuid', 'name', 'manufacturer', 'model', 'serialNumber', 'firmwareRevision' ])) } } SNMP.prototype.configurationRequestHandler = function (context, request, callback) {/* jshint unused: false */ this.log('configuration request', { context: context, request: request }) } SNMP.prototype.configureAccessory = function (accessory) { const self = this accessory.on('identify', function (paired, callback) { self.log(accessory.displayName, ': identify request') callback() }) self.discoveries[accessory.UUID] = accessory self.log('configureAccessory', underscore.pick(accessory, [ 'UUID', 'displayName' ])) } const Agent = function (platform, agentId, service) { const varbinds = service.packet && service.packet.pdu && service.packet.pdu.varbinds if (!(this instanceof Agent)) return new Agent(platform, agentId, service) this.platform = platform this.agentId = agentId this.rinfo = underscore.pick(service, [ 'host', 'port' ]) this.varbinds = varbinds this.model = varbinds[0].value // sysDescr.0 this.name = varbinds[2].value // sysName.0 this.cache = new NodeCache({ stdTTL: this.platform.config.ttl || 10 }) // TBD: set timeouts this.session = new snmpn.Session(this.rinfo) } Agent.prototype.attachAccessory = function (accessory) { this.accessory = accessory this._setServices(accessory) this.platform.log('attachAccessory', underscore.pick(this, [ 'uuid', 'name', 'manufacturer', 'model', 'serialNumber', 'firmwareRevision' ])) } // not used Agent.prototype.onlineP = function (callback) { const self = this const agent = self.platform.agents[self.agentId] const oidI = discovery.Observe.prototype.oidI // TBD: set range via options if ((agent.timestamp + (5 * 1000)) >= underscore.now) return callback(null, true) /* sysUptime.0 */ self.session.get({ oid: oidI('1.3.6.1.2.1.1.3.0') }, function (err, varbinds) { if (err) { self.platform.log.error('onlineP', underscore.extend({ agentId: self.agentId }, err)) return callback((err.toString() !== 'Error: Timeout') ? err : null, false) } self.platform.log('agent ' + self.agentId + ' sysUpTime: ' + varbinds[0].value) self.agents[self.agentId].timestamp = underscore.now() callback(null, true) }) } Agent.prototype._getState = function (property, callback) { const self = this self.cache.get('properties', function (err, properties) { const f = function (properties, cacheP) { let value if (!properties) { self.platform.log.error('getState: no properties', underscore.extend({ agentId: self.agentId, cacheP: cacheP }, err)) if (property === 'statusFault') return Characteristic.StatusFault.GENERAL_FAULT return } if (property === 'aqi') { const fpc = properties['particles.2_5'] // TBD: set range via options value = fpc < 35 ? Characteristic.AirQuality.EXCELLENT : fpc < 100 ? Characteristic.AirQuality.FAIR : Characteristic.AirQuality.POOR } else if (property === 'statusFault') { value = Characteristic.StatusFault.NO_FAULT } else { value = properties[property] } debug(self.rinfo.host + ' getState ' + property + ': ' + value) return value } if (err) return callback(err) if (properties) return callback(null, f(properties, true)) self._refresh(function (err, properties) { if (err) { if (property === 'statusFault') return Characteristic.StatusFault.GENERAL_FAULT return callback(err) } if (properties) { debug(self.rinfo.host + ' set properties cache: ' + JSON.stringify(properties, null, 2)) self.cache.set('properties', properties) } callback(null, f(properties, false)) }) }) } const ServersCheck = function (platform, agentId, service) { const self = this if (!(self instanceof ServersCheck)) return new ServersCheck(platform, agentId, service) Agent.call(self, platform, agentId, service) self.manufacturer = 'ServersCheck' self._refresh(function (err, properties) { let accessory if (err) return self.platform.log.error('refresh', underscore.extend({ agentId: self.agentId }, err)) debug(self.rinfo.host + ' set properties cache: ' + JSON.stringify(properties, null, 2)) self.cache.set('properties', properties) if (self.accessory) return accessory = self.platform.discoveries[self.uuid] if (!accessory) return self.platform._addAccessory(self) delete self.platform.discoveries[self.uuid] self.attachAccessory(accessory) accessory.updateReachability(true) }) } util.inherits(ServersCheck, Agent) ServersCheck.prototype._setServices = function (accessory) { const self = this const findOrCreateService = function (P, callback) { let newP, service service = accessory.getService(P) if (!service) { newP = true service = new P() } callback(service) if (newP) accessory.addService(service, self.name) } findOrCreateService(Service.AccessoryInformation, function (service) { service.setCharacteristic(Characteristic.Name, self.name) .setCharacteristic(Characteristic.Manufacturer, self.manufacturer) .setCharacteristic(Characteristic.Model, self.model) .setCharacteristic(Characteristic.SerialNumber, self.serialNumber) }) underscore.keys(self.capabilities).forEach(function (key) { const f = { airflow: function () { findOrCreateService(CommunityTypes.AirFlowSensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Air Flow') service.getCharacteristic(CommunityTypes.AirFlow) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , amperes: function () { findOrCreateService(PowerService, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Energy Monitor') service.getCharacteristic(CommunityTypes.Amperes) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , contactSensorState: function () { findOrCreateService(Service.ContactSensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Contact') service.getCharacteristic(Characteristic.ContactSensorState) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , dewpoint: function () { findOrCreateService(Service.TemperatureSensor, function (service) { service.getCharacteristic(CommunityTypes.DewPoint) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , humidity: function () { findOrCreateService(Service.HumiditySensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Humidity') service.getCharacteristic(Characteristic.CurrentRelativeHumidity) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , kilowattHours: function () { findOrCreateService(PowerService, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Energy Monitor') service.getCharacteristic(CommunityTypes.kilowattHours) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , leak_detect: function () { findOrCreateService(Service.LeakSensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Leak Detected') service.getCharacteristic(Characteristic.LeakDetected) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , light: function () { findOrCreateService(Service.LightSensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Ambient Light') service.getCharacteristic(Characteristic.CurrentAmbientLightLevel) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , motion: function () { findOrCreateService(Service.MotionSensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Motion Detected') service.getCharacteristic(Characteristic.MotionDetected) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , noise: function () { findOrCreateService(CommunityTypes.NoiseLevelSensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Noise Level') service.getCharacteristic(CommunityTypes.NoiseLevel) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , powerFail: function () { findOrCreateService(Service.SecuritySystem, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Power Fail') service.getCharacteristic(Characteristic.SecuritySystemCurrentState) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , 'particles.2_5': function () { findOrCreateService(Service.AirQualitySensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Air Quality') service.getCharacteristic(Characteristic.AirQuality) .on('get', function (callback) { self._getState.bind(self)('aqi', callback) }) service.getCharacteristic(Characteristic.PM2_5Density) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , temperature: function () { findOrCreateService(Service.TemperatureSensor, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Temperature') service.getCharacteristic(Characteristic.CurrentTemperature) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , volts: function () { findOrCreateService(PowerService, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Energy Monitor') service.getCharacteristic(CommunityTypes.Volts) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , voltAmperes: function () { findOrCreateService(PowerService, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Energy Monitor') service.getCharacteristic(CommunityTypes.VoltAmperes) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } , watts: function () { findOrCreateService(PowerService, function (service) { service.setCharacteristic(Characteristic.Name, self.name + ' Energy Monitor') service.getCharacteristic(CommunityTypes.Watts) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) }) } }[key] || function () { self.platform.log.warn('setServices: no Service for ' + key) } f() }) } ServersCheck.prototype._refresh = function (callback) { const self = this const oidI = discovery.Observe.prototype.oidI let oid if (!self.uuid) { arp.table(function (err, entry) { if (err) return self.platform.logger.error('ARP table', err) if (!entry) { if (!self.uuid) self.uuid = UUIDGen.generate(self.name) return } if ((self.uuid) || (entry.ip !== self.rinfo.host)) return self.uuid = UUIDGen.generate(entry.mac + ':' + self.rinfo.port) if (!self.serialNumber) self.serialNumber = entry.mac }) } oid = self.varbinds[1].value + '.3' self.session.getSubtree({ oid: oidI(oid) }, function (err, varbinds) { let name, properties if (err) { self.platform.log.error('getSubtree', underscore.extend({ agentId: self.agentId, oid: oid }, err)) return callback(err) } self.timestamp = underscore.now() name = '-' properties = {} varbinds.forEach(function (varbind) { const leaf = varbind.oid[varbind.oid.length - 2] if ((leaf % 4) === 1) name = varbind.value else if ((leaf % 4) === 2) self._normalize(properties, name, varbind.value) }) oid = self.varbinds[1].value + '.11' self.session.getSubtree({ oid: oidI(oid) }, function (err, varbinds) { let names if (err) { self.platform.log.error('getSubtree', underscore.extend({ agentId: self.agentId, oid: oid }, err)) return callback(err) } self.timestamp = underscore.now() names = {} varbinds.forEach(function (varbind) { const leaf = varbind.oid[varbind.oid.length - 2] , subtree = varbind.oid[varbind.oid.length - 3] if (leaf === 1) names[subtree] = varbind.value else if ((leaf === 2) && (!!names[subtree])) { self._normalize(properties, names[subtree], varbind.value) } }) self.capabilities = {} underscore.keys(properties).forEach(function (key) { self.capabilities[key] = { field: key } }) callback(null, properties) }) }) } /* name: Int. Temp/Ext. Temp | Airflow | Sound Meter | Humidity | Water Detect | Dust Sensor temperature | airflow | noise | humidity | liquid_detected | particles.2_5 units: 'C' | m/s | dB | RH-% | boolean | mg/m^3 datapoints: '26.51' | '0.01' | '48.93' | '36.82' | 'DRY' | '0.01' name: Dew Point | Light | Shock | Power Fail | Motion | Security dewpoint | light | vibration | | motion | contactSensorState units: 'C' | lux | m/s^2 | boolean | boolean | boolean datapoints: 43.45 | 17 | 1.06 | 'PWR OK' | '0.0' | 'ON' name: Power | Amp Meter | Watt Meter | Smoke kilowattHours | amperes | watts | smoke units: kWh | A | W | boolean datapoints: 5 | | | 'OK' */ ServersCheck.prototype._normalize = function (properties, name, value) { let f, key if ((name) && (name.substr(-1) === '1')) name = name.slice(0, -1) if ((name === '-') || (name === 'Int. Ping')) return key = { Airflow : 'airflow' , 'Amp Meter' : 'amperes' , 'Dust Sensor' : 'particles.2_5' , 'Dew Point' : 'dewpoint' , 'Ext. Temp' : 'temperature' , Flooding : 'leak_detect' , Humidity : 'humidity' , 'Int. Temp' : 'temperature' , Light : 'light' , 'Liquid Detect' : 'leak_detect' , Motion : 'motion' , Power : 'kilowattHours' , 'Power Fail' : 'powerFail' , Security : 'contactSensorState' , Shock : 'vibration' , Smoke : 'smoke' , 'Sound Meter' : 'noise' , 'Water Detect' : 'leak_detect' , 'Watt Meter' : 'watts' }[name] if (!key) return this.platform.log.warn('normalize: no entry for ' + name) const float = function () { value = parseFloat(value) if (!isNaN(value)) properties[key] = value } f = { airflow : float , amperes : function () { value = parseFloat(value) if (isNaN(value)) return // an AWFUL hack... properties.voltAmperes = 120 * value properties.volts = 120 properties[key] = value } , contactSensorState : function () { properties[key] = Characteristic.ContactSensorState[(value !== 'ON') ? 'CONTACT_NOT_DETECTED' : 'CONTACT_DETECTED'] } , dewpoint : float , humidity : float , kilowattHours : float , light : float , leak_detect : function () { properties[key] = Characteristic.LeakDetected[(value !== 'DRY') ? 'LEAK_DETECTED' : 'LEAK_NOT_DETECTED'] } , motion : function () { if (typeof properties.motion === 'undefined') properties.motion = false properties.motion |= value !== '0.00' } , noise : float , powerFail : function () { properties[key] = Characteristic.SecuritySystemCurrentState[(value !== 'PWR OK') ? 'ALARM_TRIGGERED' : 'STAY_ARM'] } , 'particles.2_5' : float , smoke : function () { properties[key] = Characteristic.SmokeDetected[(value !== 'OK') ? 'SMOKE_DETECTED' : 'SMOKE_NOT_DETECTED'] } , temperature : float , vibration : function () { value = parseFloat(value) if (isNaN(value)) return if (typeof properties.motion === 'undefined') properties.motion = false properties.motion |= (value < 0.75) || (value > 1.25) } , watts : float }[key] if (!f) return debug('no normalizer for ' + name + ' / ' + key + ' = ' + value) f() debug(name + ' -> ' + key + ': ' + value) } const UPS = function (platform, agentId, service) { const self = this if (!(self instanceof UPS)) return new UPS(platform, agentId, service) Agent.call(self, platform, agentId, service) self.upsObject = upsObjectIDs[service.packet.pdu.varbinds[1].value] || {} self._refresh(function (err, properties) { let accessory if (err) return self.platform.log.error('refresh', underscore.extend({ agentId: self.agentId }, err)) debug(self.rinfo.host + ' set properties cache: ' + JSON.stringify(properties, null, 2)) self.cache.set('properties', properties) if (self.accessory) return accessory = self.platform.discoveries[self.uuid] if (!accessory) return self.platform._addAccessory(self) delete self.platform.discoveries[self.uuid] self.attachAccessory(accessory) accessory.updateReachability(true) }) } util.inherits(UPS, Agent) UPS.prototype._setServices = function (accessory) { const self = this let alarmService, batteryService, contactService, powerService const findOrCreateService = function (P, callback) { let newP, service service = accessory.getService(P) if (!service) { newP = true service = new P() } callback(service) if (newP) accessory.addService(service, self.name) } findOrCreateService(Service.AccessoryInformation, function (service) { service.setCharacteristic(Characteristic.Name, self.name) .setCharacteristic(Characteristic.Manufacturer, self.manufacturer) .setCharacteristic(Characteristic.Model, self.model) .setCharacteristic(Characteristic.SerialNumber, self.serialNumber) .setCharacteristic(Characteristic.FirmwareRevision, self.firmwareRevision) }) if (self.upsObject.type === 'battery') { findOrCreateService(Service.SecuritySystem, function (service) { alarmService = service service.setCharacteristic(Characteristic.Name, self.name + ' Battery Fail') }) findOrCreateService(Service.BatteryService, function (service) { batteryService = service service.addOptionalCharacteristic(CommunityTypes.BatteryVoltageDC) service.addOptionalCharacteristic(Characteristic.CurrentTemperature) }) findOrCreateService(Service.ContactSensor, function (service) { contactService = service service.getCharacteristic(Characteristic.StatusFault) .on('get', function (callback) { self._getState.bind(self)('statusFault', callback) }) }) } else { findOrCreateService(PowerService, function (service) { powerService = service }) } underscore.keys(self.capabilities).forEach(function (key) { const f = { batteryFail: function () { if (alarmService) { alarmService.getCharacteristic(Characteristic.SecuritySystemCurrentState) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } } , batteryLevel: function () { if (!batteryService) return batteryService.getCharacteristic(Characteristic.BatteryLevel) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , chargingState: function () { if (!batteryService) return batteryService.getCharacteristic(Characteristic.ChargingState) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , statusLowBattery: function () { if (batteryService) { batteryService.getCharacteristic(Characteristic.StatusLowBattery) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } if (contactService) { contactService.getCharacteristic(Characteristic.StatusLowBattery) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } } , batteryVoltageDC: function () { if (!batteryService) return batteryService.getCharacteristic(CommunityTypes.BatteryVoltageDC) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , currentTemperature: function () { if (!batteryService) return batteryService.getCharacteristic(Characteristic.CurrentTemperature) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , contactSensorState: function () { if (!contactService) return contactService.getCharacteristic(Characteristic.ContactSensorState) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , statusActive: function () { if (!contactService) return contactService.getCharacteristic(Characteristic.ContactStatusActive) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , volts: function () { if (!powerService) return powerService.getCharacteristic(CommunityTypes.Volts) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , voltAmperes: function () { if (!powerService) return powerService.getCharacteristic(CommunityTypes.VoltAmperes) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , watts: function () { if (!powerService) return powerService.getCharacteristic(CommunityTypes.Watts) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , kilowattHours: function () { if (!powerService) return powerService.getCharacteristic(CommunityTypes.KilowattHours) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } , amperes: function () { if (!powerService) return powerService.getCharacteristic(CommunityTypes.Amperes) .on('get', function (callback) { self._getState.bind(self)(key, callback) }) } }[key] || function () { self.platform.log.warn('setServices: no Service for ' + key) } f() }) } UPS.prototype._refresh = function (callback) { const self = this const oidI = discovery.Observe.prototype.oidI , oidS = discovery.Observe.prototype.oidS , capabilities = {} , properties = {} const done = function () { self.capabilities = capabilities if (self.uuid) return callback(null, properties) if (!self.upsObject.initObjects) return finalize() initialize(function(err) { if (err) return callback(err) finalize() }) } const initialize = function (callback) { walk (oidI(self.upsObject.initObjects), callback) } const finalize = function () { arp.table(function (err, entry) { if (err) return self.platform.logger.error('ARP table', err) if (!entry) { if (!self.uuid) self.uuid = UUIDGen.generate(self.name) return callback(null, properties) } if ((self.uuid) || (entry.ip !== self.rinfo.host)) return self.uuid = UUIDGen.generate(entry.mac + ':' + self.rinfo.port) if (!self.serialNumber) self.serialNumber = entry.mac }) } const walk = function (oid, callback) { self.session.getSubtree({ oid: oid }, function (err, varbinds) { if (err) { self.platform.log.error('getSubtree', underscore.extend({ agentId: self.agentId, oid: oid }, err)) return callback(err) } self.timestamp = underscore.now() varbinds.forEach(function (varbind) { const name = oidS(varbind.oid) underscore.keys(upsMibMap).forEach(function (key) { const entry = upsMibMap[key] , field = entry.capability , property = entry.property if (name !== entry.name) return if (property) self[property] = varbind.value.trim() else { entry.normalize(properties, entry.capability, varbind.value) capabilities[field] = { field: field } } }) }) callback() }) } walk(oidI(upsMibMap.upsObjects.oid), function (err) { if (err) return callback(err) if (!self.upsObject.upsObjects) return done() walk(oidI(self.upsObject.upsObjects), function (err) { if (err) return callback(err) done() }) }) } const upsNormalizers = { hundredNonNegativeInteger32 : function (properties, key, value) { value = parseInt(value, 10) if (isNaN(value) || (value < 0)) return value /= 100.0 properties[key] = value.toFixed(2) } , integer32 : function (properties, key, value) { value = parseInt(value, 10) if (isNaN(value)) return properties[key] = value } , nonNegativeInteger : function (properties, key, value) { value = parseInt(value, 10) if (isNaN(value) || (value < 0)) return properties[key] = value } , percentage : function (properties, key, value) { value = parseInt(value, 10) if (isNaN(value) || (value < 0) || (value > 100)) return properties[key] = value } , tenNonNegativeInteger32 : function (properties, key, value) { value = parseInt(value, 10) if (isNaN(value) || (value < 0)) return value /= 10.0 properties[key] = value.toFixed(1) } } const upsMibMap = { upsObjects : { oid : '1.3.6.1.2.1.33.1' } , upsIdentManufacturer : { name : '1.3.6.1.2.1.33.1.1.1.0' , property : 'manufacturer' } , upsIdentModel : { name : '1.3.6.1.2.1.33.1.1.2.0' , property : 'model' } , upsIdentUPSSoftwareVersion : { name : '1.3.6.1.2.1.33.1.1.3.0' , property : 'firmwareRevision' } , upsIdentName : { name : '1.3.6.1.2.1.33.1.1.5.0' , property : 'name' } , upsBatteryStatus : { name : '1.3.6.1.2.1.33.1.2.1.0' , capability : 'statusLowBattery' , normalize : function (properties, key, value) { /* TBD: how to calculate chargingState (upsEstimatedChargeRemaining === 100) ? NOT_CHARGING : (input power === 0) ? NOT_CHARGING : CHARGING */ value = parseInt(value, 10) /* unknown(1), batteryNormal(2), batteryLow(3), batteryDepleted(4) */ if (isNaN(value) || (value < 2) || (value > 4)) return properties[key] = Characteristic.StatusLowBattery[(value !== 2) ? 'BATTERY_LEVEL_LOW' : 'BATTERY_LEVEL_NORMAL'] /* properties.batteryFail = Characteristic.SecuritySystemCurrentState[(value !== 2) ? 'ALARM_TRIGGERED' : 'STAY_ARM'] */ } } , upsEstimatedChargeRemaining : { name : '1.3.6.1.2.1.33.1.2.4.0' , capability : 'batteryLevel' , normalize : function (properties, key, value) { upsNormalizers.percentage(properties, key, value) // there doesn't appear to be a way to determine this from the UPS-MIB or the POWERNET-MIB value = properties.batteryLevel === 100 properties.chargingState = Characteristic.ChargingState[value ? 'NOT_CHARGING' : 'CHARGING'] properties.contactSensorState = Characteristic.ContactSensorState[value ? 'CONTACT_DETECTED' : 'CONTACT_NOT_DETECTED'] } } , upsBatteryVoltage : { name : '1.3.6.1.2.1.33.1.2.5.0' , capability : 'batteryVoltageDC' , normalize : upsNormalizers.tenNonNegativeInteger32 } , upsBatteryTemperature : { name : '1.3.6.1.2.1.33.1.2.7.0' , capability : 'currentTemperature' , normalize : upsNormalizers.integer32 } , upsOutputVoltage : { name : '1.3.6.1.2.1.33.1.4.4.1.2.1' , column : true , capability : 'volts' , normalize : upsNormalizers.nonNegativeInteger } , upsOutputCurrent : { name : '1.3.6.1.2.1.33.1.4.4.1.3.1' , column : true , capability : 'amperes' , normalize : function (properties, key, value) { upsNormalizers.tenNonNegativeInteger32(properties, key, value) value = properties.volts * properties.amperes properties.voltAmperes = value.toFixed(1) } } , upsOutputPower : { name : '1.3.6.1.2.1.33.1.4.4.1.4.1' , column : true , capability : 'watts' , normalize : upsNormalizers.nonNegativeInteger } /* '' : { name : '' , capability : 'kilowattHours' , normalize : ... } */ , pmmIdentSerialNumber : { name : '1.3.6.1.4.1.318.1.1.1.1.2.3.0' , property : 'serialNumber' } , upsHighPrecOutputEnergyUsage : { name : '1.3.6.1.4.1.318.1.1.1.4.3.6.0' , capability : 'watts' , normalize : upsNormalizers.hundredNonNegativeInteger32 } , upsAdvBatteryReplaceIndicator: { name : '1.3.6.1.4.1.318.1.1.1.2.2.4.0' , capability : 'batteryFail' , normalize : function (properties, key, value) { value = parseInt(value, 10) /* noBatteryNeedsReplacing(1), batteryNeedsReplacing(2) */ if (isNaN(value) || (value < 1) || (value > 2)) return properties[key] = Characteristic.SecuritySystemCurrentState[(value !== 1) ? 'ALARM_TRIGGERED' : 'STAY_ARM'] } } } const sysObjectIDs = { '1.3.6.1.4.1.17095' : ServersCheck , '1.3.6.1.4.1.318.1.3.27' : UPS /* , '1.3.6.1.4.1.850.1.1.1' : UPS */ } const upsObjectIDs = { '1.3.6.1.4.1.318.1.3.27' : // Schneider Electric (APC) { type : 'battery' , initObjects : '1.3.6.1.4.1.318.1.1.1.1.2.3' , upsObjects : '1.3.6.1.4.1.318.1.1.1' } , '1.3.6.1.4.1.850.1.1.1' : // Tripp-Lite { } }