ts-onvif
Version:
Client to ONVIF NVT devices
330 lines • 16.4 kB
JavaScript
"use strict";
/**
* Device module
* @author Andrew D.Laptev <a.d.laptev@gmail.com>
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Device = void 0;
const url_1 = __importDefault(require("url"));
const utils_1 = require("./utils");
/**
* Device methods
*/
class Device {
onvif;
#services = [];
get services() {
return this.#services;
}
media2Support = false;
#scopes = [];
get scopes() { return this.#scopes; }
#serviceCapabilities;
get serviceCapabilities() { return this.#serviceCapabilities; }
#NTP;
get NTP() { return this.#NTP; }
#DNS;
get DNS() { return this.#DNS; }
#networkInterfaces;
get networkInterfaces() { return this.#networkInterfaces; }
constructor(onvif) {
this.onvif = onvif;
}
getSystemDateAndTime() {
return this.onvif.getSystemDateAndTime();
}
setSystemDateAndTime(options) {
return this.onvif.setSystemDateAndTime(options);
}
/**
* Returns information about services of the device.
*/
async getServices({ includeCapability } = { includeCapability: true }) {
const [data] = await this.onvif.request({
body: '<GetServices xmlns="http://www.onvif.org/ver10/device/wsdl">'
+ `<IncludeCapability>${includeCapability}</IncludeCapability>`
+ '</GetServices>',
});
const result = (0, utils_1.linerase)(data).getServicesResponse;
this.#services = result.service;
// ONVIF Profile T introduced Media2 (ver20) so cameras from around 2020/2021 will have
// two media entries in the ServicesResponse, one for Media (ver10/media) and one for Media2 (ver20/media)
// This is so that existing VMS software can still access the video via the orignal ONVIF Media API
// fill Cam#uri property
this.#services.forEach((service) => {
// Look for services with namespaces and XAddr values
if (Object.prototype.hasOwnProperty.call(service, 'namespace') && Object.prototype.hasOwnProperty.call(service, 'XAddr')) {
// Only parse ONVIF namespaces. Axis cameras return Axis namespaces in GetServices
if (!service.namespace || !service.XAddr) {
return;
}
const parsedNamespace = url_1.default.parse(service.namespace);
if (parsedNamespace.hostname === 'www.onvif.org' && parsedNamespace.path) {
const namespaceSplitted = parsedNamespace.path.substring(1).split('/'); // remove leading Slash, then split
if (namespaceSplitted[1] === 'media' && namespaceSplitted[0] === 'ver20') {
// special case for Media and Media2 where cameras supporting Profile S and Profile T (2020/2021 models) have two media services
this.media2Support = true;
namespaceSplitted[1] = 'media2';
}
else if (namespaceSplitted[1] === 'ptz') {
// uppercase PTZ namespace to fit names convention
namespaceSplitted[1] = 'PTZ';
}
this.onvif.uri[namespaceSplitted[1]] = this.onvif.parseUrl(service.XAddr);
}
}
});
return result;
}
/**
* This method has been replaced by the more generic {@link Device.getServices | GetServices} method.
* For capabilities of individual services refer to the GetServiceCapabilities methods.
* @param options
* @param options.category
*/
async getCapabilities(options) {
if (!options || !options.category) {
options = { category: ['All'] };
}
const [data] = await this.onvif.request({
body: `<GetCapabilities xmlns="http://www.onvif.org/ver10/device/wsdl">${options.category.map((category) => `<Category>${category}</Category>`).join('')}</GetCapabilities>`,
});
this.onvif.capabilities = (0, utils_1.linerase)(data).getCapabilitiesResponse.capabilities;
['PTZ', 'media', 'imaging', 'events', 'device', 'analytics'].forEach((name) => {
const capabilityName = name;
if ('XAddr' in this.onvif.capabilities[capabilityName]) {
this.onvif.uri[name] = this.onvif.parseUrl(this.onvif.capabilities[capabilityName].XAddr);
}
});
// extensions, eg. deviceIO
if (this.onvif.capabilities.extension) {
Object.keys(this.onvif.capabilities.extension).forEach((ext) => {
const extensionName = ext;
// TODO think about complex extensions like `telexCapabilities` and `scdlCapabilities`
if ('XAddr' in this.onvif.capabilities.extension[extensionName]
&& this.onvif.capabilities.extension[extensionName].XAddr) {
this.onvif.uri[extensionName] = new URL(this.onvif.capabilities.extension[extensionName].XAddr);
}
});
// HACK for a Profile G NVR that has 'replay' but did not have 'recording' in GetCapabilities
if (this.onvif.uri.replay && !this.onvif.uri.recording) {
const tempRecorderXaddr = this.onvif.uri.replay.href.replace('replay', 'recording');
this.onvif.emit('warn', `Adding ${tempRecorderXaddr} for bad Profile G device`);
this.onvif.uri.recording = new URL(tempRecorderXaddr);
}
}
return this.onvif.capabilities;
}
/**
* Receive device information
*/
async getDeviceInformation() {
const [data] = await this.onvif.request({ body: '<GetDeviceInformation xmlns="http://www.onvif.org/ver10/device/wsdl"/>' });
this.onvif.deviceInformation = (0, utils_1.linerase)(data).getDeviceInformationResponse;
return this.onvif.deviceInformation;
}
/**
* Receive hostname information
*/
async getHostname() {
const [data] = await this.onvif.request({
body: '<GetHostname xmlns="http://www.onvif.org/ver10/device/wsdl"/>',
});
return (0, utils_1.linerase)(data).getHostnameResponse.hostnameInformation;
}
/**
* Receive the scope parameters of a device
*/
async getScopes() {
const [data] = await this.onvif.request({
body: '<GetScopes xmlns="http://www.onvif.org/ver10/device/wsdl"/>',
});
this.#scopes = (0, utils_1.linerase)(data).getScopesResponse.scopes;
if (this.#scopes === undefined) {
this.#scopes = [];
}
else if (!Array.isArray(this.#scopes)) {
this.#scopes = [this.#scopes];
}
return this.#scopes;
}
/**
* Set the scope parameters of a device
* @param scopes Array of scope's uris
*/
async setScopes(scopes) {
const [data] = await this.onvif.request({
body: `<SetScopes xmlns="http://www.onvif.org/ver10/device/wsdl">${scopes.map((uri) => `<Scopes>${uri}</Scopes>`).join('')}</SetScopes>`,
});
if ((0, utils_1.linerase)(data).setScopesResponse.length !== 0) {
throw new Error('Wrong `SetScopes` response');
}
// get new scopes from device
return this.getScopes();
}
/**
* Returns the capabilities of the device service. The result is returned in a typed answer
*/
async getServiceCapabilities() {
const [data] = await this.onvif.request({
body: '<GetServiceCapabilities xmlns="http://www.onvif.org/ver10/device/wsdl" />',
});
const capabilitiesResponse = (0, utils_1.linerase)(data).getServiceCapabilitiesResponse;
this.#serviceCapabilities = capabilitiesResponse.capabilities;
if (capabilitiesResponse.capabilities?.misc?.auxiliaryCommands !== undefined) {
this.#serviceCapabilities.misc.auxiliaryCommands = capabilitiesResponse.capabilities.misc.auxiliaryCommands.split(' ');
}
return this.#serviceCapabilities;
}
/**
* This operation reboots the device
*/
async systemReboot() {
return this.onvif.request({
service: 'device', // or 'deviceIO' ?
body: '<SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/>',
}).then(([data]) => data[0].systemRebootResponse[0].message[0]);
}
/**
* This operation gets the NTP settings from a device. If the device supports NTP, it shall be possible to get the
* NTP server settings through the GetNTP command.
*/
async getNTP() {
const [data] = await this.onvif.request({
service: 'device',
body: '<GetNTP xmlns="http://www.onvif.org/ver10/device/wsdl"/>',
});
this.#NTP = (0, utils_1.linerase)(data).getNTPResponse.NTPInformation;
if (this.#NTP?.NTPManual && !Array.isArray(this.#NTP.NTPManual)) {
this.#NTP.NTPManual = [this.#NTP.NTPManual];
}
if (this.#NTP?.NTPFromDHCP && !Array.isArray(this.#NTP.NTPFromDHCP)) {
this.#NTP.NTPFromDHCP = [this.#NTP.NTPFromDHCP];
}
return this.#NTP;
}
/**
* Set the NTP settings on a device
*/
async setNTP(options) {
let body = '<SetNTP xmlns="http://www.onvif.org/ver10/device/wsdl">'
+ `<FromDHCP>${options.fromDHCP ?? false}</FromDHCP>`;
if (options.NTPManual && Array.isArray(options.NTPManual)) {
options.NTPManual.forEach((NTPManual) => {
body += (NTPManual.type ? '<NTPManual>'
+ `<Type xmlns="http://www.onvif.org/ver10/schema">${NTPManual.type}</Type>${NTPManual.IPv4Address ? `<IPv4Address xmlns="http://www.onvif.org/ver10/schema">${NTPManual.IPv4Address}</IPv4Address>` : ''}${NTPManual.IPv6Address ? `<IPv6Address xmlns="http://www.onvif.org/ver10/schema">${NTPManual.IPv6Address}</IPv6Address>` : ''}${NTPManual.DNSname ? `<DNSname xmlns="http://www.onvif.org/ver10/schema">${NTPManual.DNSname}</DNSname>` : ''}${NTPManual.extension ? `<Extension xmlns="http://www.onvif.org/ver10/schema">${NTPManual.extension}</Extension>` : ''}</NTPManual>` : '');
});
}
body += '</SetNTP>';
const [data] = await this.onvif.request({
service: 'device',
body,
});
if (data['tds:SetNTPResponse'][0] !== '') {
throw new Error('Wrong `SetNTP` response');
}
return this.getNTP();
}
/**
* This operation gets the DNS settings from a device. The device shall return its DNS configurations through the
* GetDNS command.
*/
async getDNS() {
const [data] = await this.onvif.request({
service: 'device',
body: '<GetDNS xmlns="http://www.onvif.org/ver10/device/wsdl"/>',
});
this.#DNS = (0, utils_1.linerase)(data).getDNSResponse.DNSInformation;
if (this.#DNS?.DNSManual && !Array.isArray(this.#DNS.DNSManual)) {
this.#DNS.DNSManual = [this.#DNS.DNSManual];
}
if (this.#DNS?.DNSFromDHCP && !Array.isArray(this.#DNS.DNSFromDHCP)) {
this.#DNS.DNSFromDHCP = [this.#DNS.DNSFromDHCP];
}
return this.#DNS;
}
async setDNS(options) {
let body = '<SetDNS xmlns="http://www.onvif.org/ver10/device/wsdl">'
+ `<FromDHCP>\${!!options.fromDHCP}</FromDHCP>${options.searchDomain && Array.isArray(options.searchDomain) ? options.searchDomain
.map((domain) => `<SearchDomain>${domain}</SearchDomain>`).join('') : ''}`;
if (options.DNSManual && Array.isArray(options.DNSManual)) {
options.DNSManual.forEach((DNSManual) => {
body += (DNSManual.type ? '<DNSManual>'
+ `<Type xmlns="http://www.onvif.org/ver10/schema">${DNSManual.type}</Type>${DNSManual.IPv4Address ? `<IPv4Address xmlns="http://www.onvif.org/ver10/schema">${DNSManual.IPv4Address}</IPv4Address>` : ''}${DNSManual.IPv6Address ? `<IPv6Address xmlns="http://www.onvif.org/ver10/schema">${DNSManual.IPv6Address}</IPv6Address>` : ''}</DNSManual>` : '');
});
}
body += '</SetDNS>';
const [data] = await this.onvif.request({
service: 'device',
body,
});
const a = (0, utils_1.linerase)(data);
if ((0, utils_1.linerase)(data).setDNSResponse.length !== 0) {
throw new Error('Wrong `SetDNS` response');
}
return this.getDNS();
}
/**
* This operation gets the network interface configuration from a device. The device shall support return of network
* interface configuration settings as defined by the NetworkInterface type through the GetNetworkInterfaces command.
*/
async getNetworkInterfaces() {
const [data] = await this.onvif.request({
service: 'device',
body: '<GetNetworkInterfaces xmlns="http://www.onvif.org/ver10/device/wsdl"/>',
});
const { networkInterfaces } = (0, utils_1.linerase)(data, { array: ['networkInterfaces', 'manual'] }).getNetworkInterfacesResponse;
this.#networkInterfaces = Array.isArray(networkInterfaces) ? networkInterfaces : [];
return this.#networkInterfaces;
}
/**
* Set network interfaces information
*/
async setNetworkInterfaces(options) {
const { networkInterface } = options;
if (!networkInterface) {
return { rebootNeeded: false };
}
const body = '<SetNetworkInterfaces xmlns="http://www.onvif.org/ver10/device/wsdl">'
+ `<InterfaceToken>${options.interfaceToken}</InterfaceToken>`
+ '<NetworkInterface>'
+ `<Enabled xmlns="http://www.onvif.org/ver10/schema">${networkInterface.enabled}</Enabled>${networkInterface.link
? '<Link xmlns="http://www.onvif.org/ver10/schema">'
+ `<AutoNegotiation>${networkInterface.link.autoNegotiation}</AutoNegotiation>`
+ `<Speed>${networkInterface.link.speed}</Speed>`
+ `<Duplex>${networkInterface.link.duplex}</Duplex>`
+ '</Link>'
: ''}${!Number.isNaN(networkInterface.MTU) ? `<MTU xmlns="http://www.onvif.org/ver10/schema">${networkInterface.MTU}</MTU>` : ''}${networkInterface.IPv4
? '<IPv4 xmlns="http://www.onvif.org/ver10/schema">'
+ `<Enabled>${networkInterface.IPv4.enabled}</Enabled>${networkInterface.IPv4.manual ? networkInterface.IPv4.manual
.map((ipv4) => `<Manual><Address>${ipv4.address}</Address><PrefixLength>${ipv4.prefixLength}</PrefixLength></Manual>`).join('') : ''}<DHCP>${networkInterface.IPv4.DHCP}</DHCP>`
+ '</IPv4>'
: ''}${networkInterface.IPv6
? '<IPv6 xmlns="http://www.onvif.org/ver10/schema">'
+ `<Enabled>${networkInterface.IPv6.enabled}</Enabled>`
+ `<AcceptRouterAdvert >${networkInterface.IPv6.acceptRouterAdvert}</AcceptRouterAdvert>`
+ `${networkInterface.IPv6.manual ? networkInterface.IPv6.manual
.map((ipv6) => `<Manual><Address>${ipv6.address}</Address><PrefixLength>${ipv6.prefixLength}</PrefixLength></Manual>`).join('') : ''}`
+ `<DHCP>${networkInterface.IPv6.DHCP}</DHCP>`
+ '</IPv6>'
: ''}</NetworkInterface>`
+ '</SetNetworkInterfaces>';
const [data] = await this.onvif.request({
service: 'device',
body,
});
const result = (0, utils_1.linerase)(data).setNetworkInterfacesResponse;
if (Array.isArray(networkInterface.IPv6?.manual) && networkInterface.IPv6.manual.length > 0) {
this.onvif.hostname = networkInterface.IPv6.manual[0].address;
}
if (Array.isArray(networkInterface.IPv4?.manual) && networkInterface.IPv4.manual.length > 0) {
this.onvif.hostname = networkInterface.IPv4.manual[0].address;
}
return result;
}
}
exports.Device = Device;
//# sourceMappingURL=device.js.map