Shinobi/tools/onvifUtilsForTest.js

547 lines
20 KiB
JavaScript

// relies on https://gitlab.com/Shinobi-Systems/shinobi-onvif
const onvif = require('shinobi-onvif');
const getFunctionParamNames = function(func) {
var fnStr = func.toString().replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg, '');
var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(/([^\s,]+)/g);
if(result === null)
result = [];
return result;
}
const mergeDeep = function(...objects) {
const isObject = obj => obj && typeof obj === 'object';
return objects.reduce((prev, obj) => {
Object.keys(obj).forEach(key => {
const pVal = prev[key];
const oVal = obj[key];
if (Array.isArray(pVal) && Array.isArray(oVal)) {
prev[key] = pVal.concat(...oVal);
}
else if (isObject(pVal) && isObject(oVal)) {
prev[key] = mergeDeep(pVal, oVal);
}
else {
prev[key] = oVal;
}
});
return prev;
}, {});
}
const replaceDynamicInOptions = (Camera,options) => {
const newOptions = {}
Object.keys(options).forEach((key) => {
const value = options[key]
if(typeof value === 'string'){
newOptions[key] = value.replace(/__CURRENT_TOKEN/g,Camera.current_profile.token)
}else if(value !== undefined && value !== null){
newOptions[key] = value
}
})
return newOptions
}
const runOnvifMethod = async (onvifOptions) => {
return new Promise((resolve,reject) => {
const onvifDevice = onvifOptions.device
var response = {ok: false}
var errorMessage = function(msg,error){
response.ok = false
response.msg = msg
response.error = error
resolve(response)
}
var actionCallback = function(onvifActionResponse){
response.ok = true
if(onvifActionResponse.data){
response.responseFromDevice = onvifActionResponse.data
}else{
response.responseFromDevice = onvifActionResponse
}
if(onvifActionResponse.soap)response.soap = onvifActionResponse.soap
resolve(response)
}
var isEmpty = function(obj) {
for(var key in obj) {
if(obj.hasOwnProperty(key))
return false;
}
return true;
}
var completeAction = function(command){
if(command && command.then){
command.then(actionCallback).catch(function(error){
errorMessage('Device Action responded with an error',error)
})
}else if(command){
response.ok = true
response.repsonseFromDevice = command
resolve(response)
}else{
response.error = 'Big Errors, Please report it to Shinobi Development'
resolve(response)
}
}
var action
if(onvifOptions.service){
if(onvifDevice.services[onvifOptions.service] === undefined){
return errorMessage('This is not an available service. Please use one of the following : '+Object.keys(onvifDevice.services).join(', '))
}
if(onvifDevice.services[onvifOptions.service] === null){
return errorMessage('This service is not activated. Maybe you are not connected through ONVIF. You can test by attempting to use the "Control" feature with ONVIF in Shinobi.')
}
action = onvifDevice.services[onvifOptions.service][onvifOptions.action]
}else{
action = onvifDevice[onvifOptions.action]
}
if(!action || typeof action !== 'function'){
errorMessage(onvifOptions.action+' is not an available ONVIF function. See https://github.com/futomi/node-onvif for functions.')
}else{
var argNames = getFunctionParamNames(action)
var options
var command
if(argNames[0] === 'options' || argNames[0] === 'params'){
options = replaceDynamicInOptions(onvifDevice,onvifOptions.options || {})
response.options = options
}
if(onvifOptions.service){
command = onvifDevice.services[onvifOptions.service][onvifOptions.action](options)
}else{
command = onvifDevice[onvifOptions.action](options)
}
completeAction(command)
}
})
}
const getDeviceInformation = async (onvifDevice,options) => {
// const options = {
// protocols: true,
// networkInterface: true,
// gateway: true,
// dns: true,
// ntp: true,
// }
const response = {
ok: true,
}
try{
if(options.all || options.protocols){
response.protocols = (await onvifDevice.services.device.getNetworkProtocols()).data.GetNetworkProtocolsResponse.NetworkProtocols
}
if(options.all || options.networkInterface){
response.networkInterface = (await onvifDevice.services.device.getNetworkInterfaces()).data.GetNetworkInterfacesResponse.NetworkInterfaces
}
if(options.all || options.gatewayAddress){
response.gateway = (await onvifDevice.services.device.getNetworkDefaultGateway()).data.GetNetworkDefaultGatewayResponse.NetworkGateway.IPv4Address
}
if(options.all || options.dns){
response.dns = (await onvifDevice.services.device.getDNS()).data.GetDNSResponse.DNSInformation
}
if(options.all || options.ntp){
response.ntp = (await onvifDevice.services.device.getNTP()).data.GetNTPResponse.NTPInformation
}
if(options.all || options.date){
response.date = (await onvifDevice.services.device.getSystemDateAndTime()).data.GetSystemDateAndTimeResponse.SystemDateAndTime
}
if(options.all || options.users){
response.users = (await onvifDevice.services.device.getUsers()).data.GetUsersResponse.User
}
if(options.all || options.hostname){
response.hostname = (await onvifDevice.services.device.getHostname()).data.GetHostnameResponse.HostnameInformation.Name
}
if(options.all || options.discoveryMode){
response.discoveryMode = (await onvifDevice.services.device.getDiscoveryMode()).data.GetDiscoveryModeResponse.DiscoveryMode
}
if(options.all || options.videoEncoders){
response.videoEncoders = (await onvifDevice.services.media.getVideoEncoderConfigurations()).data.GetVideoEncoderConfigurationsResponse.Configurations
}
if(options.all || options.videoEncoderOptions){
response.videoEncoderOptions = (await runOnvifMethod({
device: onvifDevice,
action: 'getVideoEncoderConfigurationOptions',
service: 'media',
options: {}
})).responseFromDevice.GetVideoEncoderConfigurationOptionsResponse.Options
}
if(options.all || options.imagingSettings){
const imagingSettings = (await runOnvifMethod({
device: onvifDevice,
action: 'getImagingSettings',
service: 'imaging',
options: {"ConfigurationToken":"__CURRENT_TOKEN"}
}));
response.imagingSettings = imagingSettings.responseFromDevice ? imagingSettings.responseFromDevice.GetImagingSettingsResponse.ImagingSettings : imagingSettings
}
}catch(err){
response.ok = false
response.error = err.stack.toString()
console.log(err)
console.log(onvifDevice)
}
return response
}
const setProtocols = async (onvifDevice,saveSet) => {
// const saveSet = {
// "HTTP": "80",
// "RTSP": "554",
// }
const response = {
ok: false
}
try{
const saveKeys = Object.keys(saveSet)
const protocols = (await getDeviceInformation(onvifDevice,{protocols: true})).protocols
protocols.forEach((item) => {
saveKeys.forEach((key) => {
if(item.Name === key.toUpperCase()){
const port = saveSet[key] && !isNaN(saveSet[key]) ? parseInt(saveSet[key]) : item.Name === 'RTSP' ? 554 : 80
console.log('PORT',port)
item.Port = port
item.Enabled = true
}
})
})
console.log('protocols',protocols)
const onvifResponse = await onvifDevice.services.device.setNetworkProtocols({
NetworkProtocols: protocols
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setNetworkInterface = async (onvifDevice,options) => {
// const options = {
// interface: 'eth0',
// ipv4: '10.1.103.158',
// dhcp: false,
// }
const response = {
ok: false
}
try{
const { networkInterface } = await getDeviceInformation(onvifDevice,{networkInterface: true})
const onvifParams = {
SetNetworkInterfaces: {
InterfaceToken: options.interface || networkInterface.$.token,
NetworkInterface: {
Enabled: true,
IPv4: {
Enabled: true,
Manual: {
Address: options.ipv4 || networkInterface.IPv4.Config.Manual.Address,
PrefixLength: '24',
},
'DHCP': options.dhcp === undefined || options.dhcp === null ? networkInterface.IPv4.Config.DHCP === 'true' ? true : false : options.dhcp,
}
}
}
}
const onvifResponse = await onvifDevice.services.device.setNetworkInterfaces(onvifParams)
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setGateway = async (onvifDevice,options) => {
// const options = {
// ipv4: '1.1.1.1,8.8.8.8',
// }
const response = {
ok: false
}
try{
const gatewayAddress = (await getDeviceInformation(onvifDevice,{gateway: true})).gateway
const onvifResponse = await onvifDevice.services.device.setNetworkDefaultGateway({
'NetworkGateway': [
{'IPv4Address': options.ipv4 || gatewayAddress}
]
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setDNS = async (onvifDevice,options) => {
options = Object.assign({
dns: '1.1.1.1,8.8.8.8',
searchDomain: 'localhost',
dhcp: false,
},options)
const response = {
ok: false
}
try{
const dnsArray = []
const searchDomain = (options.searchDomain || '').split(',')
const dnsList = (options.dns || '1.1.1.1').split(',')
dnsList.forEach((item) => {
dnsArray.push({
Type: 'IPv4',
IPv4Address: item,
})
})
const dnsInfo = (await getDeviceInformation(onvifDevice,{dns: true})).dns
const onvifResponse = await onvifDevice.services.device.setDNS({
'FromDHCP' : !options.dhcp ? false : true,
'SearchDomain': searchDomain,
'DNSManual' : dnsArray
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setNTP = async (onvifDevice,options) => {
// const options = {
// ipv4: '1.1.1.1,8.8.8.8',
// dhcp: false,
// }
const response = {
ok: false
}
try{
const ntpInfo = (await getDeviceInformation(onvifDevice,{ntp: true})).ntp
const ipv4 = options.ipv4 || ntpInfo.NTPManual.IPv4Address
const onvifResponse = await onvifDevice.services.device.setNTP({
FromDHCP: !options.dhcp ? false : true,
NTPManual: {'Type': "IPv4", 'IPv4Address': ipv4}
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setHostname = async (onvifDevice,options) => {
// const options = {
// name: 'hostname',
// }
const response = {
ok: false
}
try{
const hostname = options.name || (await getDeviceInformation(onvifDevice,{hostname: true})).hostname
const onvifResponse = await onvifDevice.services.device.setHostname({
Name: hostname
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const rebootCamera = async (onvifDevice,options) => {
const response = {
ok: false
}
try{
const onvifResponse = await onvifDevice.services.device.reboot()
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setDateAndTime = async (onvifDevice,options) => {
// const options = {
// dateTimeType: 'ntp',
// daylightSavings: false,
// timezone: 'XXXX',
// utcDateTime: 'XXXX',
// }
const response = {
ok: false
}
try{
// const dateInfo = await onvifDevice.services.device.getSystemDateAndTime().GetSystemDateAndTimeResponse.SystemDateAndTime
const onvifResponse = await onvifDevice.services.device.setSystemDateAndTime ({
DateTimeType: options.dateTimeType,
DaylightSavings: !options.daylightSavings ? false : true,
// TimeZone: options.timezone,
UTCDateTime: new Date(options.utcDateTime),
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const createUser = async (onvifDevice,options) => {
// const options = {
// name: 'user1',
// password: '123',
// level: 'Administrator' || 'Operator' || 'User',
// }
const response = {
ok: false
}
try{
const onvifResponse = await onvifDevice.services.device.createUsers({
'User' : [
{'Username': options.name, 'Password' : options.password, 'UserLevel': options.level}
]
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const deleteUser = async (onvifDevice,options) => {
// const options = {
// name: 'user1',
// }
const response = {
ok: false
}
try{
const onvifResponse = await onvifDevice.services.device.deleteUsers({
'User' : [
{'Username': options.name}
]
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const validateEncoderOptions = (chosenOptions, videoEncoderOptions) => {
const resolutions = []
const minQuality = parseInt(videoEncoderOptions.QualityRange.Min) || 1
const maxQuality = parseInt(videoEncoderOptions.QualityRange.Max) || 6
const quality = !isNaN(chosenOptions.Quality) ? parseInt(chosenOptions.Quality) : 1
const minGovLength = parseInt(videoEncoderOptions.H264.GovLengthRange.Min) || 0
const maxGovLength = parseInt(videoEncoderOptions.H264.GovLengthRange.Max) || 100
const govLength = !isNaN(chosenOptions.H264.GovLength) ? parseInt(chosenOptions.H264.GovLength) : 10
const minFrameRateLimit = parseInt(videoEncoderOptions.H264.FrameRateRange.Min) || 1
const maxFrameRateLimit = parseInt(videoEncoderOptions.H264.FrameRateRange.Max) || 30
const frameRateLimit = !isNaN(chosenOptions.RateControl.FrameRateLimit) ? parseInt(chosenOptions.RateControl.FrameRateLimit) : 15
const minEncodingInterval = parseInt(videoEncoderOptions.H264.EncodingIntervalRange.Min) || 1
const maxEncodingInterval = parseInt(videoEncoderOptions.H264.EncodingIntervalRange.Max) || 30
const encodingInterval = !isNaN(chosenOptions.RateControl.EncodingInterval) ? parseInt(chosenOptions.RateControl.EncodingInterval) : 1
videoEncoderOptions.H264.ResolutionsAvailable.forEach((resolution) => {
resolutions.push(`${resolution.Width}x${resolution.Height}`)
})
if(resolutions.indexOf(`${chosenOptions.Resolution.Width}x${chosenOptions.Resolution.Height}`) === -1){
chosenOptions.Resolution.Width = resolutions[0].Width
chosenOptions.Resolution.Height = resolutions[0].Height
}
if(quality < minQuality || quality > maxQuality){
chosenOptions.Quality = 1
}
if(govLength < minGovLength || govLength > maxGovLength){
chosenOptions.H264.GovLength = 10
}
if(frameRateLimit < minFrameRateLimit || frameRateLimit > maxFrameRateLimit){
chosenOptions.RateControl.FrameRateLimit = 15
}
if(encodingInterval < minEncodingInterval || encodingInterval > maxEncodingInterval){
chosenOptions.RateControl.EncodingInterval = 1
}
}
const setVideoConfiguration = async (onvifDevice,options) => {
const response = {
ok: false
}
try{
const {
videoEncoders,
videoEncoderOptions
} = await getDeviceInformation(onvifDevice,{
videoEncoders: true,
videoEncoderOptions: true
})
const videoEncoderIndex = {}
videoEncoders.forEach((encoder) => {videoEncoderIndex[encoder.$.token] = encoder})
const chosenToken = `${options.videoToken || videoEncoders[0].$.token}`
const videoEncoder = videoEncoderIndex[chosenToken] || {}
const onvifParams = mergeDeep(videoEncoder,options,{
Multicast: {
AutoStart: !options.Multicast || !options.Multicast.AutoStart ? 'false' : 'true'
},
})
const validatedEncoderOptions = validateEncoderOptions(onvifParams, videoEncoderOptions)
const onvifResponse = await onvifDevice.services.media.setVideoEncoderConfiguration({
Configuration: onvifParams,
ConfigurationToken: chosenToken
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setImagingSettings = async (onvifDevice,options) => {
const response = {
ok: false
}
try{
const { imagingSettings } = await getDeviceInformation(onvifDevice,{imagingSettings: true})
const onvifParams = mergeDeep(imagingSettings,options)
const onvifResponse = await onvifDevice.services.imaging.setImagingSettings({
ConfigurationToken: options.videoToken || onvifDevice.current_profile.token,
ImagingSettings: onvifParams
})
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const setDiscoveryMode = async (onvifDevice,options) => {
const response = {
ok: false
}
try{
const onvifResponse = await onvifDevice.services.device.setDiscoveryMode(options.mode === 'Discoverable' || options.mode === 'NonDiscoverable' ? options.mode : 'Discoverable')
response.ok = true
response.onvifResponse = onvifResponse
}catch(err){
response.error = err.stack.toString()
}
return response
}
const getUIFieldValues = async (onvifDevice) => {
return await getDeviceInformation(onvifDevice,{
all: true,
})
}
module.exports = {
mergeDeep,
getFunctionParamNames,
getDeviceInformation: getDeviceInformation,
setHostname: setHostname,
setProtocols: setProtocols,
setGateway: setGateway,
setDNS: setDNS,
setNTP: setNTP,
rebootCamera: rebootCamera,
setDateAndTime: setDateAndTime,
createUser: createUser,
deleteUser: deleteUser,
setVideoConfiguration: setVideoConfiguration,
setImagingSettings: setImagingSettings,
setDiscoveryMode: setDiscoveryMode,
setNetworkInterface: setNetworkInterface,
getUIFieldValues: getUIFieldValues,
}