UNPKG

ts-onvif

Version:

Client to ONVIF devices

574 lines (573 loc) 17.1 kB
/** * Onvif module * @author Andrew D.Laptev <a.d.laptev@gmail.com> * @see https://www.onvif.org/wp-content/uploads/2022/07/ONVIF_Device_Feature_Discovery_Specification_21.12.pdf */ import { EventEmitter } from 'events'; import { SecureContextOptions } from 'tls'; import { Agent as HttpsAgent, RequestOptions } from 'https'; import http, { Agent as HttpAgent } from 'http'; import { OnvifResponse } from './utils'; import Device from './device'; import type Media from './media'; import type Media2 from './media2'; import type PTZ from './ptz'; import { Capabilities, Profile, SystemDateTime } from './interfaces/onvif'; import { GetDeviceInformationResponse, SetSystemDateAndTime } from './interfaces/devicemgmt'; import { ReferenceToken } from './interfaces/common'; import Events, { NotificationMessage } from './events'; import type Replay from './replay'; import type Imaging from './imaging'; import type Recording from './recording'; import type DoorControl from './doorcontrol'; import type AccessControl from './accesscontrol'; import type Credential from './credential'; import type AccessRules from './accessrules'; import type Schedule from './schedule'; import type Provisioning from './provisioning'; import type AdvancedSecurity from './advancedsecurity'; import type Thermal from './thermal'; import type Analytics from './analytics'; import type DeviceIO from './deviceio'; import type Display from './display'; import type ActionEngine from './actionengine'; import type Search from './search'; import type AnalyticsDevice from './analyticsdevice'; import type Receiver from './receiver'; /** * Cam constructor options */ export interface OnvifOptions { /** Set true if using `https` protocol, defaults to false. */ useSecure?: boolean; /** Set options for https like ca, cert, ciphers, rejectUnauthorized, secureOptions, secureProtocol, etc. */ secureOptions?: SecureContextOptions; /** Use WS-Security SOAP headers */ useWSSecurity?: boolean; hostname: string; username?: string; password?: string; port?: number; path?: string; timeout?: number; urn?: string; /** Supports things like https://www.npmjs.com/package/proxy-agent which provide SOCKS5 and other connections. */ agent?: HttpsAgent | HttpAgent | boolean; /** Force using hostname and port from constructor for the services (ex.: for proxying), defaults to false. */ preserveAddress?: boolean; /** Set false if the camera should not connect automatically, defaults false. */ autoConnect?: boolean; } export interface OnvifServices { ptz?: URL; analytics?: URL; analyticsdevice?: URL; device?: URL; deviceIO?: URL; display?: URL; events?: URL; imaging?: URL; media2?: URL; media?: URL; receiver?: URL; recording?: URL; replay?: URL; doorcontrol?: URL; accesscontrol?: URL; credential?: URL; accessrules?: URL; schedule?: URL; provisioning?: URL; advancedsecurity?: URL; thermal?: URL; actionengine?: URL; search?: URL; [key: string]: URL | undefined; } export interface OnvifRequestOptions extends Omit<RequestOptions, 'headers'> { headers?: http.OutgoingHttpHeaders; /** Name of service (ptz, media, etc) */ service?: keyof OnvifServices; /** SOAP body */ body: Record<string, any>; /** Defines another url to request */ url?: URL; /** Make request to PTZ uri or not */ ptz?: boolean; /** Timeout for pull-point event requests */ timeout?: number; /** Additional SOAP-headers */ soapHeaders?: Record<string, any>; /** Tags that should be forced to be arrays */ array?: string[]; /** Values of these tags will be in xml2js format */ rawXML?: string[]; } export interface OnvifRawRequestOptions extends Omit<OnvifRequestOptions, 'body'> { /** SOAP body */ body: string; } /** * Information about active video source */ export interface ActiveSource { sourceToken: ReferenceToken; /** * Media profile token */ profileToken: ReferenceToken; videoSourceConfigurationToken: ReferenceToken; videoSourceToken: ReferenceToken; encoding?: string; width?: number; height?: number; fps?: number; bitrate?: number; ptz?: { name: string; token: ReferenceToken; }; } export interface SetSystemDateAndTimeExtended extends SetSystemDateAndTime { /** * Javascript Date object to use instead of UTCDateTime */ dateTime?: Date; /** * The TZ format is specified by POSIX, please refer to POSIX 1003.1 section 8.3 * Example: Europe, Paris TZ=CET-1CEST,M3.5.0/2,M10.5.0/3 * CET = designation for standard time when daylight saving is not in force * -1 = offset in hours = negative so 1 hour east of Greenwich meridian * CEST = designation when daylight saving is in force ("Central European Summer Time") * , = no offset number between code and comma, so default to one hour ahead for daylight saving * M3.5.0 = when daylight saving starts = the last Sunday in March (the "5th" week means the last in the month) * /2, = the local time when the switch occurs = 2 a.m. in this case * M10.5.0 = when daylight saving ends = the last Sunday in October. * /3, = the local time when the switch occurs = 3 a.m. in this case */ timezone?: string; } export interface SystemDateTimeExtended extends SystemDateTime { /** * Javascript Date object to use instead of UTCDateTime */ dateTime: Date; } interface OnvifEvents { [Onvif.EVENT]: [msg: NotificationMessage]; [Onvif.ERROR]: [error: Error]; [Onvif.CONNECT]: []; [Onvif.RAW_REQUEST]: [xml: string, requestOptions: http.RequestOptions]; [Onvif.REQUEST_BODY]: [body: string]; [Onvif.RAW_RESPONSE]: [xml: string]; [Onvif.WARN]: [error: Error]; [Onvif.EVENTS_ERROR]: [error: Error]; newListener: [event: string, listener: (...args: any[]) => void]; removeListener: [event: string, listener: (...args: any[]) => void]; } export declare class Onvif extends EventEmitter<OnvifEvents> { /** * Indicates raw xml response from device. * @event rawResponse * @example * ```typescript * onvif.on('rawResponse', (xml) => { console.log('<- response was', xml); }); * ``` */ static readonly RAW_RESPONSE = "rawResponse"; /** * Indicates raw xml request to device. * @event rawRequest * @example * ```typescript * onvif.on('rawRequest', (xml) => { console.log('-> request was', xml); }); * ``` */ static readonly RAW_REQUEST = "rawRequest"; /** * Shows body of request * @event */ static readonly REQUEST_BODY = "requestBody"; /** * Indicates any errors except events errors * @event error * @example * ```typescript * onvif.on('error', console.error); * ``` */ static readonly ERROR = "error"; /** * Indicates events errors * @event eventsError * @example * ```typescript * onvif.on('eventsError', console.error); * ``` */ static readonly EVENTS_ERROR = "eventsError"; /** * Indicates any event from Onvif device. * @event event * @example * ```typescript * onvif.on('event', (msg) => { console.log(new Date().toLocaleTimeString(), 'new event', msg); }); * ``` */ static readonly EVENT = "event"; /** * Indicates any warnings * @event warn * @example * ```typescript * onvif.on('warn', console.warn); * ``` */ static readonly WARN = "warn"; /** * Indicates successfully connection * @event connect * @example * ```typescript * onvif.on('connect', () => console.log('connected!')); * ``` */ static readonly CONNECT = "connect"; /** * Core device namespace for device v1.0 methods * @example * ```typescript * const date = await onvif.device.getSystemDateAndTime(); * console.log(date.toLocaleString()); * ``` */ readonly device: Device; /** * Media namespace for media v1.0 methods * @example * ```typescript * const profiles = await onvif.media.getProfiles(); * console.log(profiles); * ``` */ readonly media: Media; /** * Media2 namespace for media2 v1.0 methods * @example * ```typescript * const profiles = await onvif.media2.getProfiles(); * console.log(profiles); * ``` */ readonly media2: Media2; /** * PTZ namespace for ptz v1.0 methods * @example * ```typescript * const ptz = await onvif.ptz.getPTZStatus(); * console.log(ptz); * ``` */ readonly ptz: PTZ; /** * Events namespace for events v1.0 methods * @example * ```typescript * onvif.on('event', (msg) => { console.log('-> request was', xml); }); * ``` */ readonly events: Events; /** * Replay namespace for replay v1.0 methods * @example * ```typescript * const replay = await onvif.replay.getReplayConfiguration(); * console.log(replay); * ``` */ readonly replay: Replay; /** * Imaging namespace for imaging v1.0 methods * @example * ```typescript * const imaging = await onvif.imaging.getImagingSettings(); * console.log(imaging); * ``` */ readonly imaging: Imaging; /** * Recording namespace for recording v1.0 methods * @example * ```typescript * const recording = await onvif.recording.getRecordingConfiguration(); * console.log(recording); * ``` */ readonly recording: Recording; /** * DoorControl namespace for doorcontrol v1.0 methods * @example * ```typescript * const doorControl = await onvif.doorControl.getDoorControlConfiguration(); * console.log(doorControl); * ``` */ readonly doorControl: DoorControl; /** * AccessControl namespace for accesscontrol v1.0 methods * @example * ```typescript * const list = await onvif.accessControl.getAccessPointInfoList(); * console.log(list); * ``` */ readonly accessControl: AccessControl; /** * Credential namespace for credential v1.0 methods * @example * ```typescript * const list = await onvif.credential.getCredentialInfoList(); * console.log(list); * ``` */ readonly credential: Credential; /** * AccessRules namespace for accessrules v1.0 methods * @example * ```typescript * const list = await onvif.accessRules.getAccessProfileInfoList(); * console.log(list); * ``` */ readonly accessRules: AccessRules; /** * Schedule namespace for schedule v1.0 methods * @example * ```typescript * const list = await onvif.schedule.getScheduleInfoList(); * console.log(list); * ``` */ readonly schedule: Schedule; /** * Provisioning namespace for provisioning v1.0 methods * @example * ```typescript * const caps = await onvif.provisioning.getServiceCapabilities(); * console.log(caps); * ``` */ readonly provisioning: Provisioning; /** * AdvancedSecurity namespace for advancedsecurity v1.0 methods * @example * ```typescript * const caps = await onvif.advancedSecurity.getServiceCapabilities(); * console.log(caps); * ``` */ readonly advancedSecurity: AdvancedSecurity; /** * Thermal namespace for thermal v1.0 methods * @example * ```typescript * const thermal = await onvif.thermal.getConfigurations(); * console.log(thermal); * ``` */ readonly thermal: Thermal; /** * Analytics namespace for analytics v1.0 methods * @example * ```typescript * const analytics = await onvif.analytics.getAnalyticsConfiguration(); * console.log(analytics); * ``` */ readonly analytics: Analytics; /** * DeviceIO namespace for deviceio v1.0 methods * @example * ```typescript * const deviceIO = await onvif.deviceIO.getDeviceIOConfiguration(); * console.log(deviceIO); * ``` */ readonly deviceIO: DeviceIO; /** * Display namespace for display v1.0 methods * @example * ```typescript * const display = await onvif.display.getDisplayConfiguration(); * console.log(display); * ``` */ readonly display: Display; /** * ActionEngine namespace for actionengine v1.0 methods * @example * ```typescript * const actionEngine = await onvif.actionEngine.getActionEngineConfiguration(); * console.log(actionEngine); * ``` */ readonly actionEngine: ActionEngine; /** * Search namespace for search v1.0 methods * @example * ```typescript * const summary = await onvif.search.getRecordingSummary(); * console.log(summary); * ``` */ readonly search: Search; /** * AnalyticsDevice namespace for analytics device v1.0 methods * @example * ```typescript * const controls = await onvif.analyticsDevice.getAnalyticsEngineControls(); * console.log(controls); * ``` */ readonly analyticsDevice: AnalyticsDevice; /** * Receiver namespace for receiver v1.0 methods * @example * ```typescript * const receivers = await onvif.receiver.getReceivers(); * console.log(receivers); * ``` */ readonly receiver: Receiver; /** * Indicates if the device is using secure connection */ readonly useSecure: boolean; /** * Secure options for the connection */ secureOptions: SecureContextOptions; /** * Use WS-Security for the connection (this is the default and adds security headers in the SOAP messages) */ useWSSecurity: boolean; /** * Nonce for the connection */ private nc; /** * Hostname of the ONVIF device */ hostname: string; /** * Username for the connection */ username?: string; /** * Password for the connection */ password?: string; /** * Port for the connection */ port: number; /** * Path for the connection */ path: string; timeout: number; agent: HttpsAgent | HttpAgent | boolean; preserveAddress: boolean; uri: OnvifServices; private timeShift?; capabilities: Capabilities; defaultProfiles: Profile[]; defaultProfile?: Profile; private activeSources; activeSource?: ActiveSource; readonly urn?: string; deviceInformation?: GetDeviceInformationResponse; constructor(options: OnvifOptions); envelopeBody(body: Record<string, any>): { $: { 'xmlns:xsi': string; 'xmlns:xsd': string; }; }; /** * Envelope header for all SOAP messages * @param options * @private */ envelopeHeader(options?: OnvifRequestOptions): { Security?: { $: { 's:mustUnderstand': string; xmlns: string; }; UsernameToken: { Username: string | undefined; Password: { $: { Type: string; }; _: string; }; Nonce: { $: { EncodingType: string; }; _: string; }; Created: { $: { xmlns: string; }; _: string; }; }; } | undefined; }; private passwordDigest; private rawRequest; private digestAuth; request(options: OnvifRequestOptions): OnvifResponse; private parseChallenge; private updateNC; /** * Parse url with an eye on `preserveAddress` property * @param address * @private */ parseUrl(address: string): URL; /** * Receive date and time from cam */ getSystemDateAndTime(): Promise<SystemDateTimeExtended>; /** * Receive only date and time from cam (old behaviour, returns only Date object) */ getOnlySystemDateAndTime(): Promise<Date>; /** * Add time shift to use with ONVIF timestamps * @param data * @private */ private setupSystemDateAndTime; /** * Set the device system date and time * Supports two possible date and time values: UTCDateTime(ONVIF types) or dateTime(js Date-object, preferred) */ setSystemDateAndTime(options: SetSystemDateAndTimeExtended): Promise<SystemDateTimeExtended>; /** * Check and find out video configuration for device * @private */ private getActiveSources; /** * Connect to the camera and fill device information properties */ connect(): Promise<this>; } export {};