UNPKG

axis-discovery-ssdp

Version:

A Node.js SSDP (UPnP) client library written in TypeScript capable of searching for Axis Communication cameras.

283 lines 11.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Discovery = void 0; const expect = require("@fantasticfiasco/expect"); const events_1 = require("events"); const logging_1 = require("./logging"); const network_interfaces_1 = require("./network-interfaces"); const options_1 = require("./options"); const root_descriptions_1 = require("./root-descriptions"); const sockets_1 = require("./sockets"); /** * Class responsible for discovering Axis cameras on the network. */ class Discovery { /** * Initializes a new instance of the class. * @param options The SSDP discovery options. */ constructor(options) { this.eventEmitter = new events_1.EventEmitter(); this.options = options || {}; } /** * Start listen for device advertisements on all network interface * addresses. */ start() { return __awaiter(this, void 0, void 0, function* () { expect.toNotExist(this.sockets, 'Discovery has already been started'); (0, logging_1.log)('Discovery#start'); yield this.setup(); }); } /** * Stop listening for device advertisements. */ stop() { return __awaiter(this, void 0, void 0, function* () { expect.toExist(this.sockets, 'Discovery has not been started'); (0, logging_1.log)('Discovery#stop'); yield this.teardown(); }); } /** * Triggers a new search for devices on the network. */ search() { return __awaiter(this, void 0, void 0, function* () { if (!this.sockets) { throw new Error('Discovery has not been started'); } (0, logging_1.log)('Discovery#search'); for (const socket of this.sockets) { if (socket instanceof sockets_1.MSearchSocket) { yield socket.search(); } } }); } /** * Alias for on(eventName, listener). */ addListener(eventName, listener) { this.eventEmitter.addListener(eventName, listener); return this; } /** * Adds the listener function to the end of the listeners array for the event named eventName. * No checks are made to see if the listener has already been added. Multiple calls passing the * same combination of eventName and listener will result in the listener being added, and * called, multiple times. * @param eventName The name of the event. * @param listener The callback function. */ on(eventName, listener) { this.eventEmitter.on(eventName, listener); return this; } /** * Adds a one-time listener function for the event named eventName. The next time eventName is * triggered, this listener is removed and then invoked. * @param eventName The name of the event. * @param listener The callback function. */ once(eventName, listener) { this.eventEmitter.once(eventName, listener); return this; } /** * Alias for off(eventName, listener). * @param eventName The name of the event. * @param listener The callback function. */ removeListener(eventName, listener) { this.eventEmitter.removeListener(eventName, listener); return this; } /** * Removes the specified listener from the listener array for the event named eventName. * @param eventName The name of the event. * @param listener The callback function. */ off(eventName, listener) { this.eventEmitter.off(eventName, listener); return this; } /** * Removes all listeners, or those of the specified eventName. * @param eventName The name of the event. */ removeAllListeners(eventName) { this.eventEmitter.removeAllListeners(eventName); return this; } /** * By default EventEmitters will print a warning if more than 10 listeners are added for a * particular event. This is a useful default that helps finding memory leaks. The * emitter.setMaxListeners() method allows the limit to be modified for this specific * EventEmitter instance. The value can be set to Infinity (or 0) to indicate an unlimited * number of listeners. */ setMaxListeners(n) { this.eventEmitter.setMaxListeners(n); return this; } /** * Returns the current max listener value for the EventEmitter which is either set by * emitter.setMaxListeners(n) or defaults to EventEmitter.defaultMaxListeners. */ getMaxListeners() { return this.eventEmitter.getMaxListeners(); } /** * Returns a copy of the array of listeners for the event named eventName. * @param eventName The name of the event. */ // eslint-disable-next-line @typescript-eslint/ban-types listeners(eventName) { return this.eventEmitter.listeners(eventName); } /** * Returns a copy of the array of listeners for the event named eventName, including any * wrappers (such as those created by once()). * @param eventName The name of the event. */ // eslint-disable-next-line @typescript-eslint/ban-types rawListeners(eventName) { return this.eventEmitter.rawListeners(eventName); } /** * Synchronously calls each of the listeners registered for the event named eventName, in the * order they were registered, passing the supplied arguments to each. * @param eventName The name of the event. */ emit(eventName, args) { return this.eventEmitter.emit(eventName, args); } /** * Returns the number of listeners listening to the event named eventName. * @param eventName The name of the event. */ listenerCount(eventName) { return this.eventEmitter.listenerCount(eventName); } /** * Adds the listener function to the beginning of the listeners array for the event named * eventName. No checks are made to see if the listener has already been added. Multiple calls * passing the same combination of eventName and listener will result in the listener being * added, and called, multiple times. * @param eventName The name of the event. * @param listener The callback function. */ prependListener(eventName, listener) { this.eventEmitter.prependListener(eventName, listener); return this; } /** * Adds a one-time listener function for the event named eventName to the beginning of the * listeners array. The next time eventName is triggered, this listener is removed, and then * invoked. * @param eventName The name of the event. * @param listener The callback function. */ prependOnceListener(eventName, listener) { this.eventEmitter.prependOnceListener(eventName, listener); return this; } /** * Returns an array listing the events for which the emitter has registered listeners. The * values in the array are strings or Symbols. */ eventNames() { return this.eventEmitter.eventNames(); } setup() { return __awaiter(this, void 0, void 0, function* () { this.sockets = []; const addresses = (0, network_interfaces_1.getIPv4Addresses)(); (0, logging_1.log)('Discovery#setup - interface addresses: %o', addresses); // Passive SSDP yield this.setupSocket(this.sockets, new sockets_1.NotifySocket(addresses)); // Active SSDP for (const address of addresses) { yield this.setupSocket(this.sockets, new sockets_1.MSearchSocket(address)); } }); } setupSocket(sockets, socket) { return __awaiter(this, void 0, void 0, function* () { sockets.push(socket); socket.on('hello', (message) => this.onHelloMessage(message)); socket.on('goodbye', (message) => this.onGoodbyeMessage(message)); yield socket.start(); }); } teardown() { return __awaiter(this, void 0, void 0, function* () { if (this.sockets) { for (const socket of this.sockets) { this.teardownSocket(socket); } this.sockets = undefined; } }); } teardownSocket(socket) { return __awaiter(this, void 0, void 0, function* () { socket.removeAllListeners('hello'); socket.removeAllListeners('goodbye'); yield socket.stop(); }); } onHelloMessage(message) { (0, logging_1.log)('Discovery#onHelloMessage - %s', message.remoteAddress); const device = (0, sockets_1.mapFromMessage)(message); if (device) { // Emit initial hello this.eventEmitter.emit('hello', device); // Request root description this.requestRootDescription(message.remoteAddress, message.location); } else { (0, logging_1.log)('Discovery#onHelloMessage - ignore %s since mapping was unsuccessful', message.remoteAddress); } } onGoodbyeMessage(message) { (0, logging_1.log)('Discovery#onGoodbyeMessage - %s', message.remoteAddress); const device = (0, sockets_1.mapFromMessage)(message); if (device) { this.eventEmitter.emit('goodbye', device); } else { (0, logging_1.log)('Discovery#onGoodbyeMessage - ignore %s since mapping was unsuccessful', message.remoteAddress); } } requestRootDescription(remoteAddress, location) { return __awaiter(this, void 0, void 0, function* () { try { const httpClient = this.options.httpClient || new options_1.HttpClient(); const rootDescriptionRequest = new root_descriptions_1.RootDescriptionRequest(remoteAddress, location, httpClient); const rootDescription = yield rootDescriptionRequest.send(); const device = (0, root_descriptions_1.mapFromRootDescription)(rootDescription); if (device !== null) { this.eventEmitter.emit('hello', device); } } catch (error) { (0, logging_1.log)('Discovery#requestRootDescription - %o', error); } }); } } exports.Discovery = Discovery; //# sourceMappingURL=Discovery.js.map