UNPKG

onvif-nvt-ts

Version:

Wrapper for ONVIF spec to control NVT (Network Video Transitter) devices. Forked and added TypeScript support from onvif-nvt.

412 lines 19.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const url_parse_1 = __importDefault(require("url-parse")); const soap_1 = __importDefault(require("../utils/soap")); const Util = __importStar(require("../utils/util")); /** * @class * <p> * {@link https://www.onvif.org/ver10/events/wsdl/event.wsdl}<br> * {@link https://www.onvif.org/wp-content/uploads/2017/07/ONVIF_Event_Handling_Test_Specification_v17.06.pdf}<br> * </p> * <h3>Functions</h3> * createPullPointSubscription, * {@link Events#getEventProperties}, * {@link Events#getServiceCapabilities}, * pullMessages, * seek, * {@link Events#setSynchronizationPoint} * <br><br> * <h3>Overview</h3> * An event is an action or occurrence detected by a device that a client can subscribe to.<br> * Events are handled through the event service. This specification defines event handling * based on the [WS-BaseNotification] and [WS-Topics] specifications. It extends the event * notion to allow clients to track object properties (such as digital input and motion alarm * properties) through events. Properties are defined in Section 9.4.<br> * The description of event payload and their filtering within subscriptions is discussed in section * 9.5. Section 9.6 describes how a synchronization point can be requested by clients using one * of the three notification interfaces. Section 9.7 describes the integration of Topics and section * 9.10 discusses the handling of faults.<br> * Section 9.11 demonstrates the usage of the Real-Time Pull-Point Notification Interface * including Message Filtering and Topic Set. Examples for the basic notification interface can * be found in the corresponding [WS-BaseNotification] specification.<br> * An ONVIF compliant device shall provide an event service as defined in [ONVIF Event WSDL].<br> * Both device and client shall support [WS-Addressing] for event services. * <br><br> * <h3>Push Events</h3> * <ul> * <li> -> GetEventProperties</li> * <li> <- GetEventPropertiesResponse</li> * <li> -> Subscribe (specifying URI of your server)</li> * <li> <- Notify</li> * <li> -> Renew (specifying termination time)</li> * <li> <- RenewResponse</li> * <li> -> Unsubscribe</li> * <li> <- UnsubscribeResponse</li> * </ul> * <h3>Pull Events</h3> * <ul> * <li> -> GetEventProperties</li> * <li> <- GetEventPropertiesResponse</li> * <li> -> CreatePullPointSubscription</li> * <li> <- CreatePullPointSubscriptionResponse</li> * <li> -> PullMessages</li> * <li> -> PullMessagesResponse</li> * <li> -> Renew (specifying termination time)</li> * <li> <- RenewResponse</li> * <li> -> Unsubscribe</li> * <li> <- UnsubscribeResponse</li> * </ul> */ class Events extends events_1.EventEmitter { constructor() { super(...arguments); this.soap = new soap_1.default(); this.timeDiff = 0; this.serviceAddress = null; this.username = null; this.password = null; this.namespaceAttributes = ['xmlns:tev="http://www.onvif.org/ver10/events/wsdl"']; this.intervalId = null; } /** * Call this function directly after instantiating an Events object. * @param {number} timeDiff The onvif device's time difference. * @param {object} serviceAddress An url object from url package - require('url'). * @param {string=} username Optional only if the device does NOT have a user. * @param {string=} password Optional only if the device does NOT have a password. */ init(timeDiff, serviceAddress, username, password) { this.timeDiff = timeDiff; this.serviceAddress = serviceAddress; this.username = username; this.password = password; } /** * Private function for creating a SOAP request. * @param {string} body The body of the xml. * @param {object=} subscriptionId Used internally. */ createRequest(body, subscriptionId) { const request = { body: body, xmlns: this.namespaceAttributes, diff: this.timeDiff, username: this.username, password: this.password }; if (subscriptionId) { request.subscriptionId = subscriptionId; } return this.soap.createRequest(request); } async buildRequest(methodName, xml, subscriptionId) { let errMsg = ''; if (typeof methodName === 'undefined' || methodName === null) { throw new Error('The "methodName" argument for buildRequest is required.'); } if ((errMsg = Util.isInvalidValue(methodName, 'string'))) { throw new Error('The "methodName" argument for buildRequest is invalid:' + errMsg); } let soapBody = ''; if (methodName === 'PullMessages') { soapBody = xml; } else if (typeof xml === 'undefined' || xml === null || xml === '') { soapBody += `<tev:${methodName}/>`; } else { soapBody += `<tev:${methodName}>`; soapBody += xml; soapBody += `</tev:${methodName}>`; } const soapEnvelope = this.createRequest(soapBody, subscriptionId); let serviceAddress = this.serviceAddress; // if this is a PullMessages message, it will have a specific URI for the subscription if (methodName === 'PullMessages') { serviceAddress = new url_parse_1.default(subscriptionId.Address); } return await this.soap.makeRequest('events', serviceAddress, methodName, soapEnvelope); } /** * Start a PullMessages event loop<br> * This method does a <strong>createPullPointSubscription</strong> followed by a <strong>pullMessages</strong>. * The resulting xml from the camera is then sent via an <strong>emit('messages', data)</strong>. * On error, <strong>emit('messages:error', error)</strong> * @param {integer=} loopTimeMS The amount of time between polls (in milliseconds; default 10000). * @param {string=} timeout The timeout to send to the server (using PT interval format - ex: PT1S is one second; default 'PT1M'). * @param {integer=} messageLimit The message limit to retrive (default 1) * @example * camera.events.on('messages', messages => { * console.log('Messages Received:', messages) * }) * * camera.events.on('messages:error', error => { * console.error('Messages Error:', error) * }) * * camera.events.startPull() */ startPull(loopTimeMS, timeout, messageLimit) { // set defaults if nothing was passed in loopTimeMS = loopTimeMS || 10000; timeout = timeout || 'PT1M'; messageLimit = messageLimit || 1; const getAll = () => { this._getMessages(timeout, messageLimit) .then(results => { this.emit('messages', results); // console.log(results) }) .catch(error => { this.emit('messages:error', error); }); }; this.intervalId = setInterval(() => { getAll(); }, loopTimeMS); } /** * Stops a PullMessages event loop that has been started with <strong>start()</strong>. */ stopPull() { clearInterval(this.intervalId); this.intervalId = null; } _getMessages(timeout, messageLimit) { return new Promise((resolve, reject) => { this._createPullPointSubscription() .then(results => { this._pullMessages(results, timeout, messageLimit) .then(results => { resolve(results); }) .catch(error => { reject(error); }); }) .catch(error => { reject(error); }); }); } _createPullPointSubscription() { return new Promise((resolve, reject) => { this.createPullPointSubscription() .then(results => { console.log('CreatePullPointSubscription successful'); if (!('data' in results)) { return reject(new Error('No "data" field in this.createPullPointSubscription')); } const response = results.data.CreatePullPointSubscriptionResponse; const reference = response.SubscriptionReference; let subscriptionId = {}; if (reference.ReferenceParameters) { subscriptionId = reference.ReferenceParameters.SubscriptionId; } subscriptionId.Address = reference.Address; resolve(subscriptionId); }) .catch(error => { reject(error); }); }); } _pullMessages(subscriptionId, timeout, messageLimit) { return new Promise((resolve, reject) => { this.pullMessages(subscriptionId, timeout, messageLimit) .then(results => { resolve(results); }) .catch(error => { reject(error); }); }); } // --------------------------------------------- // Events API // --------------------------------------------- /** * Event Type: Pull Event<br> * This method returns a PullPointSubscription that can be polled using PullMessages. This message contains the same elements as the SubscriptionRequest of the WS-BaseNotification without the ConsumerReference.<br> * If no Filter is specified the pullpoint notifies all occurring events to the client.<br> * This method is mandatory. * @param filter Optional XPATH expression to select specific topics. * @param initialTerminationTime Optional Initial termination time (in milliseconds) * @param subscriptionPolicy Optional Refer to {@link http://docs.oasis-open.org/wsn/wsn-ws_base_notification-1.3-spec-os.htm Web Services Base Notification 1.3 (WS-BaseNotification)}. */ async createPullPointSubscription(filter, initialTerminationTime, subscriptionPolicy) { // Example createPullPointSubscriptionRequest // <CreatePullPointSubscription xmlns="http://www.onvif.org/ver10/events/wsdl"> // <Filter> // <TopicExpression // Dialect="http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet" // xmlns="http://docs.oasis-open.org/wsn/b-2" // xmlns:tns1="http://www.onvif.org/ver10/topics">tns1:VideoSource/MotionAlarm // </TopicExpression> // </Filter> // <InitialTerminationTime>PT10M</InitialTerminationTime> // </CreatePullPointSubscription> let errMsg = ''; if (typeof filter !== 'undefined' && filter !== null) { if ((errMsg = Util.isInvalidValue(filter, 'string'))) { throw new Error('The "filter" argument for createPullPointSubscription is invalid: ' + errMsg); } } if (typeof initialTerminationTime !== 'undefined' && initialTerminationTime !== null) { if ((errMsg = Util.isInvalidValue(initialTerminationTime, 'string'))) { throw new Error('The "initialTerminationTime" argument for createPullPointSubscription is invalid: ' + errMsg); } } if (typeof subscriptionPolicy !== 'undefined' && subscriptionPolicy !== null) { if ((errMsg = Util.isInvalidValue(subscriptionPolicy, 'string'))) { throw new Error('The "subscriptionPolicy" argument for createPullPointSubscription is invalid: ' + errMsg); } } let soapBody = ''; if (typeof filter !== 'undefined' && filter !== null) { soapBody += '<tev:Filter>' + filter + '</tev:Filter>'; } if (typeof initialTerminationTime !== 'undefined' && initialTerminationTime !== null) { soapBody += '<tev:InitialTerminationTime>' + 'PT' + initialTerminationTime / 1000 + 'S' + '</tev:InitialTerminationTime>'; } if (typeof subscriptionPolicy !== 'undefined' && subscriptionPolicy !== null) { soapBody += '<tev:SubscriptionPolicy>' + subscriptionPolicy + '</tev:SubscriptionPolicy>'; } return await this.buildRequest('CreatePullPointSubscription', soapBody); } /** * Event Type: Agnostic<br> * The WS-BaseNotification specification defines a set of OPTIONAL WS-ResouceProperties. This specification does not require the implementation of the WS-ResourceProperty interface. Instead, the subsequent direct interface shall be implemented by an ONVIF compliant device in order to provide information about the FilterDialects, Schema files and topics supported by the device. */ getEventProperties() { // Example GetEventPropertiesRequest // <GetEventProperties xmlns="http://www.onvif.org/ver10/events/wsdl" /> return this.buildRequest('GetEventProperties', null, null); } /** * Event Type: Agnostic<br> * Returns the capabilities of the event service. The result is returned in a typed answer. */ getServiceCapabilities() { return this.buildRequest('GetServiceCapabilities', null, null); } /** * Event Type: Pull Event<br> * This method pulls one or more messages from a PullPoint. The device shall provide the following PullMessages command for all SubscriptionManager endpoints returned by the CreatePullPointSubscription command. This method shall not wait until the requested number of messages is available but return as soon as at least one message is available.<br> * The command shall at least support a Timeout of one minute. In case a device supports retrieval of less messages than requested it shall return these without generating a fault. * @param {object} subscriptionId An object after createPullPointSubscription is called. * @param {object} subscriptionId.Address Address from createPullPointSubscription is called. * @param {object} subscriptionId.Address._ Raw subscriptionId from createPullPointSubscription. * @param {object} subscriptionId.Address.$ Raw subscriptionId from createPullPointSubscription. * @param {integer} timeout [msec] Maximum time to block until this method returns. * @param {integer} messageLimit Upper limit for the number of messages to return at once. A server implementation may decide to return less messages. */ async pullMessages(subscriptionId, timeout, messageLimit) { // Example PullMessagesRequest // <PullMessages xmlns="http://www.onvif.org/ver10/events/wsdl"> // <Timeout>PT1S</Timeout> // <MessageLimit>1</MessageLimit> // </PullMessages> let errMsg = ''; // if ((errMsg = Util.isInvalidValue(subscriptionReferenceAddress, 'string'))) { // reject(new Error('The "subscriptionReferenceAddress" argument for pullMessages is invalid: ' + errMsg)) // return // } if ((errMsg = Util.isInvalidValue(timeout, 'string'))) { throw new Error('The "timeout" argument for pullMessages is invalid: ' + errMsg); } if ((errMsg = Util.isInvalidValue(messageLimit, 'integer'))) { throw new Error('The "messageLimit" argument for pullMessages is invalid: ' + errMsg); } let soapBody = ''; soapBody = '<tev:PullMessages>'; soapBody += '<tev:Timeout>'; soapBody += timeout; soapBody += '</tev:Timeout>'; soapBody += '<tev:MessageLimit>'; soapBody += messageLimit.toString(); soapBody += '</tev:MessageLimit>'; soapBody += '</tev:PullMessages>'; return await this.buildRequest('PullMessages', soapBody, subscriptionId); } /** * Event Type: Agnostic<br> */ renew() { // Example RenewRequest // <Renew xmlns="http://docs.oasis-open.org/wsn/b-2"> // <TerminationTime>PT10M</TerminationTime> // </Renew> return this.buildRequest('Renew'); } // notify (callback) { // return this.buildRequest('Notify', null, null, callback) // } /** * Event Type: Push Event<br> * The device shall provide the following Unsubscribe command for all SubscriptionManager * endpoints returned by the CreatePullPointSubscription command.<br> * This command shall terminate the lifetime of a pull point. */ subscribe() { // Example SubscribeRequest // <Subscribe xmlns="http://docs.oasis-open.org/wsn/b-2"> // <ConsumerReference> // <wsa:Address>http://192.168.0.111:10000/onvif/events</wsa:Address> // </ConsumerReference> // <Filter> // <TopicExpression // Dialect="http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet" // xmlns:tns1="http://www.onvif.org/ver10/topics">tns1:VideoSource/MotionAlarm // </TopicExpression> // </Filter> // <InitialTerminationTime>PT10M</InitialTerminationTime> // </Subscribe> return this.buildRequest('Subscribe'); } /** * Event Type: Agnostic<br> * The device shall provide the following Unsubscribe command for all SubscriptionManager * endpoints returned by the CreatePullPointSubscription command.<br> * This command shall terminate the lifetime of a pull point. */ unsubscribe() { // Example UnsubscribeRequest // <Unsubscribe xmlns="http://docs.oasis-open.org/wsn/b-2" /> return this.buildRequest('Unsubscribe'); } /** * Event Type: Agnostic<br> * Properties inform a client about property creation, changes and deletion in a uniform way. When a client wants to synchronize its properties with the properties of the device, it can request a synchronization point which repeats the current status of all properties to which a client has subscribed. The PropertyOperation of all produced notifications is set to “Initialized”. The Synchronization Point is requested directly from the SubscriptionManager which was returned in either the SubscriptionResponse or in the CreatePullPointSubscriptionResponse. The property update is transmitted via the notification transportation of the notification interface. This method is mandatory. */ setSynchronizationPoint() { return this.buildRequest('SetSynchronizationPoint'); } } exports.default = Events; //# sourceMappingURL=events.js.map