UNPKG

iobroker.nanoleaf-lightpanels

Version:
134 lines (112 loc) 3.8 kB
var dgram = require("dgram"); var util = require("util"); var events = require("events"); var _ = require("underscore"); const SSDP_PORT = 1900; const SSDP_MSEARCHREPLY_PORT = 5000; const BROADCAST_ADDR = "239.255.255.250"; const SSDP_ALIVE = 'ssdp:alive'; const SSDP_BYEBYE = 'ssdp:byebye'; const SSDP_UPDATE = 'ssdp:update'; const SSDP_ALL = 'ssdp:all'; const SSDP_NTS_EVENTS = { 'ssdp:alive': 'DeviceAvailable', 'ssdp:byebye': 'DeviceUnavailable', 'ssdp:update': 'DeviceUpdate' }; const UPNP_FIELDS = ['host', 'server', 'location', 'st', 'usn', 'nts', 'nt', 'bootid.upnp.org', 'configid.upnp.org', 'nextbootid.upnp.org', 'searchport.upnp.org']; function messageLines(msg) { return msg.toString('ascii').split('\r\n'); } function toKeyPair(header) { var result; var splitCharIndex = header.indexOf(':'); if (splitCharIndex > 0) { result = {}; result[header.slice(0,splitCharIndex).toLowerCase().trim()] = header.slice(splitCharIndex + 1).trim(); } return result; } function mSearchResponseParser(msg, rinfo) { var headers = messageLines(msg); // add address to headers headers.push("address: " + rinfo.address); if (headers[0] === 'HTTP/1.1 200 OK') { return _.chain(headers) .map(toKeyPair) .compact() .reduce(function (memo, obj) {return _.extend(memo, obj)}, {}) .value(); } return void 0; } function notifyResponseParser(msg, rinfo) { var headers = messageLines(msg); // add address to headers headers.push("address: " + rinfo.address); if (headers[0] === 'NOTIFY * HTTP/1.1') { return _.chain(headers) .map(toKeyPair) .compact() .reduce(function (memo, obj) {return _.extend(memo, obj)}, {}) .value(); } return void 0; } function announceDiscoveredDevice(emitter) { return function (msg, rinfo) { var device = mSearchResponseParser(msg, rinfo); if (device) { emitter.emit('DeviceFound', device); } }; } function announceDevice(emitter) { return function (msg, rinfo) { var device = notifyResponseParser(msg, rinfo); if (device) { emitter.emit(SSDP_NTS_EVENTS[device.nts], device); emitter.emit(SSDP_NTS_EVENTS[device.nts]+':'+device.nt, device); } }; } function Ssdp(interface) { events.EventEmitter.call(this); var udpServer = dgram.createSocket({ type: 'udp4', reuseAddr: true }, announceDevice(this)); udpServer.bind(SSDP_PORT, function onConnected() { udpServer.addMembership(BROADCAST_ADDR, interface); }); this.close = function(callback) { udpServer.close(callback); } this.mSearch = function(st) { if (typeof st !== 'string') { st = SSDP_ALL; } var message = "M-SEARCH * HTTP/1.1\r\n"+ "Host:"+BROADCAST_ADDR+":"+SSDP_PORT+"\r\n"+ "ST:"+st+"\r\n"+ "Man:\"ssdp:discover\"\r\n"+ "MX:2\r\n\r\n"; var mSearchListener = dgram.createSocket({ type: 'udp4', reuseAddr: true }, announceDiscoveredDevice(this)); var mSearchRequester = dgram.createSocket({ type: 'udp4', reuseAddr: true }); mSearchListener.on('listening', function () { mSearchRequester.send(new Buffer(message, "ascii"), 0, message.length, SSDP_PORT, BROADCAST_ADDR, function closeMSearchRequester() { mSearchRequester.close(); }); }); mSearchRequester.on('listening', function () { mSearchListener.bind(mSearchRequester.address().port); }); mSearchRequester.bind(SSDP_MSEARCHREPLY_PORT, interface); // MX is set to 2, wait for 1 additional sec. before closing the server setTimeout(function(){ mSearchListener.close(); }, 3000); }; } util.inherits(Ssdp, events.EventEmitter); module.exports.start = function(interface) { return new Ssdp(interface); };