UNPKG

node-red-contrib-axis-com

Version:
719 lines (657 loc) 19.1 kB
//Copyright (c) 2021-2024 Fred Juhlin const fs = require("fs"); const VapixDigest = require("./vapix-digest.js"); const VapixParser = require("./vapix-parser.js"); var exports = module.exports = {}; exports.HTTP_Get = function( device, cgi, responseType, callback ) { VapixDigest.HTTP_Get( device, cgi, responseType, function( error, body ) { callback( error, body ); }); } exports.HTTP_Post = function( device, cgi, payload, responseType, callback ) { VapixDigest.HTTP_Post( device, cgi, payload, responseType, function( error, body ) { if( error ) { callback(error,body); return; } try { body = JSON.parse(body); } catch { } callback( error, body ); }); } exports.HTTP_Put = function( device, cgi, payload, responseType, callback ) { VapixDigest.HTTP_Put( device, cgi, payload, responseType, function( error, body ) { callback( error, body ); }); } exports.HTTP_Patch = function( device, cgi, payload, responseType, callback ) { VapixDigest.HTTP_Patch( device, cgi, payload, responseType, function( error, body ) { callback( error, body ); }); } exports.SOAP = function( device, soapBody, callback ) { VapixDigest.Soap( device, soapBody, function( error, body ) { if( error ) { callback( error, body ); return; } VapixParser.SoapParser( body, function(error,data){ callback( error, data ); }); }); } exports.CGI = function( device, cgi, callback ) { VapixDigest.HTTP_Get( device, cgi, "text", function( error, body ) { if(error) { callback(error,body); return; } if( body.search("Error") >= 0 ) { callback( true, { statusCode: 500, statusMessage: "Error", body: body }); return; } callback( error, body ); }); } exports.CGI_Post = function( device, cgi, request, callback ) { VapixDigest.HTTP_Post( device, cgi, request, "json", function( error, body ) { if( error ) { callback(error,body); return; } callback( error, body ); }); } exports.JPEG = function( device, profile, callback ) { VapixDigest.HTTP_Get( device, '/axis-cgi/jpg/image.cgi?' + profile, "buffer", function( error, body ) { callback( error, body ); }); } exports.Param_Get = function( device, paramPath, callback ) { if( !paramPath || paramPath.length === 0 || paramPath.toLowerCase ( ) === "root" ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: paramPath + " is not valid" }); return; } exports.HTTP_Get( device, '/axis-cgi/param.cgi?action=list&group=' + paramPath, "text", function( error, body ) { if( error ) { callback( error, body ); return; } callback( false, VapixParser.param2json(body) ); }); } exports.Param_Set = function( device, group, parameters, callback ) { if( !group || group.length == 0 ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing parameter group" }); return; } if( !parameters || !(typeof parameters === 'object') ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing parameters must be an object" }); return; } var cgi = '/axis-cgi/param.cgi?action=update'; for( var parameter in parameters ) { var value = parameters[parameter]; if( value === true ) value = 'yes'; if( value === false ) value = 'no' if( typeof parameters[parameter] === 'object' ) { //Don't update sub groups } else { cgi += '&root.' + group + '.' + parameter + '=' + encodeURIComponent(value); } } exports.CGI( device, cgi, function( error, body ) { if( error ) { callback( error, body ); return; } callback( false, body ); }); } exports.DeviceInfo = function( device, callback ) { var info = {}; exports.Param_Get( device, "brand", function( error, response ) { if( error ) { callback( error, response ); return; } info.name = response.ProdShortName || null; info.model = response.ProdNbr || null; info.type = response.ProdType || null; exports.Param_Get( device, "network", function( error, response ) { if( error ) { callback( error, response ); return; } info.hostname = response.HostName || ""; info.hostname = response.VolatileHostName.HostName || info.hostname; if( response.hasOwnProperty("eth0") ) { info.IPv4 = response.eth0.IPAddress || ""; info.IPv6 = response.eth0.IPv6.IPAddresses || ""; info.mac = response.eth0.MACAddress || ""; exports.Param_Get( device, "properties", function( error, response ) { if( error ) { callback( error, response ); return; } info.firmware = response.Firmware && response.Firmware.Version ? response.Firmware.Version:""; info.camera = response.Image && response.Image.Format ? true: false; info.audio = response.Audio && response.Audio.Audio ? true: false; info.serial = response.System && response.System.SerialNumber ? response.System.SerialNumber : ""; info.platform = response.System && response.System.Architecture ? response.System.Architecture:""; if( response.System && response.System.hasOwnProperty("Soc") ) { var items = response.System.Soc.split(' '); if( items.length > 1 ) info.chipset = items[1]; else info.chipset = response.System.Soc; } info.hardware = response.System && response.System.HardwareID ? response.System.HardwareID:""; callback(false,info); }); } }); }); } exports.Syslog = function( device, callback ) { VapixDigest.HTTP_Get( device, '/axis-cgi/systemlog.cgi', "text", function(error, response) { if( error ) { callback( error, response ); return; } var list = VapixParser.Syslog2List( response ); callback( false, list ); }); } exports.GetTime = function( device, callback ) { var body = { "apiVersion": "1.0", "context": "NodeRed", "method": "getDateTimeInfo" }; exports.CGI_Post( device, "/axis-cgi/time.cgi", body, function(error, response ) { callback( error, response ); }); } exports.Connections = function( device, callback ) { exports.CGI( device, '/axis-cgi/admin/connection_list.cgi?action=get', function(error, response) { if( error ) { callback( error, response ); return; } var rows = response.split('\n'); var list = []; for( var i = 1; i < rows.length; i++) { var row = rows[i].trim(); row = row.replace(/\s{2,}/g, ' '); if( row.length > 10 ) { var items = row.split(' '); var ip = items[0].split('.'); if( ip != '127' ) { list.push({ address: items[0], protocol: items[1], port: items[2], service: items[3].split('/')[1] }) } } } callback( false, list ); }); } exports.Location_Get = function( device, callback ) { exports.CGI( device, '/axis-cgi/geolocation/get.cgi', function(error, response) { if( error ) { callback( error, response ); return; } VapixParser.Location( response, function(error, data ) { callback( error, data ); }); }); } exports.Location_Set = function( device, data, callback ) { var location = data; if( typeof data === "string" && data[0] === '{') location = JSON.parse(data); if( !location || !location.hasOwnProperty("longitude") || !location.hasOwnProperty("latitude") || !location.hasOwnProperty("direction") || !location.hasOwnProperty("text") ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing longitude, latitude, direction or text" }); return; } var cgi = "/axis-cgi/geolocation/set.cgi?"; latSign = ""; if( location.latitude < 0 ) { location.latitude = -location.latitude; latSign = "-"; } var latInt = parseInt(location.latitude); var latZ = ""; if( latInt < 10 ) latZ = "0"; lngSign = ""; if( location.longitude < 0 ) { location.longitude = -location.longitude; lngSign = "-"; } var lngInt = parseInt(location.longitude); var lngZ = "00"; if( lngInt >= 10 ) lngZ = "0"; if( lngInt >= 100 ) lngZ = ""; cgi += "lat=" + latSign + latZ + parseFloat(location.latitude).toFixed(8); cgi += "&lng=" + lngSign + lngZ + parseFloat(location.longitude).toFixed(8); cgi += "&heading=" + location.direction; cgi += "&text=" + encodeURIComponent(location.text); exports.CGI( device, cgi, function(error, response) { if( error ) { callback( error, response ); return; } if( response.search("Success") > 0 ) { callback(false,"OK"); return; } callback(true, response); }); } exports.ACAP_List = function( device, callback ) { exports.CGI( device, '/axis-cgi/applications/list.cgi', function(error, response) { if( error ) { callback( error, response ); return; } VapixParser.AcapList2JSON(response, function(error, data) { callback( error, data ); }); }); } exports.ACAP_Control = function( device, action, acapID, callback ) { if( !action || action.length == 0 ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing action" }); return; } if( !acapID || acapID.length == 0 || acapID.length > 20 ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing ACAP package name/id" }); return; } var cgi = '/axis-cgi/applications/control.cgi?action=' + action + '&package=' + acapID; VapixDigest.HTTP_Get( device, cgi, "text", function( error, response ) { if( error ) { callback( error, response ); return; } response = response.trim(); switch( response ) { case "OK": case "Error: 6": //Application is already running case "Error: 7": //Application is not running callback( false, "OK"); break; case "Error: 4": callback(true,{ statusCode: 400, statusMessage: "Invalid ACAP", body: response }); break; default: callback(true,{ statusCode: 400, statusMessage: "Unknown response", body: response }); break; } }); } exports.Account_List = function( device, callback) { exports.CGI( device, '/axis-cgi/pwdgrp.cgi?action=get', function( error, response ) { if( error ) { callback( true, response ); return; } VapixParser.Accounts2JSON( response, function( error, json ) { callback(error,json); }); }); }; exports.Account_Set = function( device, options, callback) { if( !options ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing account data" }); return; } if( typeof options !== "string" && typeof options !== "object" ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing account data" }); return; } account = options; if( typeof account === "string" ) account = JSON.parse(account); if( !account || !account.hasOwnProperty("name") || !account.hasOwnProperty("privileges") || !account.hasOwnProperty("password") ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing account name, priviliges or passoword" }); return; } var cgi = '/axis-cgi/pwdgrp.cgi?action=update&user=' + account.name + '&pwd=' + encodeURIComponent(account.password); VapixDigest.HTTP_Get( device, cgi, "text", function( error, response ) { if( !error && response.search("Error") < 0 ) { callback(false,response); return; } var sgrp = "viewer"; if( account.privileges.toLowerCase() === "viewer" || account.privileges.toLowerCase() === "player" ) sgrp = "viewer"; if( account.privileges.toLowerCase() === "operator" || account.privileges.toLowerCase() === "client" ) sgrp = "viewer:operator:ptz"; if( account.privileges.toLowerCase() === "admin" || account.privileges.toLowerCase() === "administrator" ) sgrp = "viewer:operator:admin:ptz"; if( account.privileges.toLowerCase() === "api" ) sgrp = "operator:admin"; cgi = '/axis-cgi/pwdgrp.cgi?action=add&user=' + account.name + '&pwd=' + encodeURIComponent(account.password) + '&grp=users&sgrp=' + sgrp + '&comment=node'; VapixDigest.HTTP_Get( device, cgi, "text", function( error, response ) { if( error ) { callback( error, response ); return; } if( response.search("Error") >= 0 ) { callback(true,response); return; } callback( false, response ); return; }); }); }; exports.Account_Remove = function( device, accountName, callback) { if( !accountName || typeof accountName !== "string" || accountName.length === 0 || accountName.length > 64) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing account name" }); return; } var cgi = "/axis-cgi/pwdgrp.cgi?action=remove&user=" + accountName; exports.CGI( device, cgi, function( error, response ) { if( error ) { callback(error, response ); return; } callback(false,response); }); }; exports.Upload_Firmare = function( device , fileData, callback ) { VapixDigest.upload( device, "firmware", "firmware.bin", null, fileData, function( error, response) { if( error ) { callback( error, response ); return; } if( typeof response === "object" && response.hasOwnProperty("error") ){ callback(true, { statusCode: response.error.code, statusMessage: response.error.message, body: response }); return; } if( typeof response === "object" && response.hasOwnProperty("data") ){ callback(false, device.address + " updated to " + response.data.firmwareVersion); return; } VapixDigest.upload( device, "firmware_legacy", "firmware.bin", null, fileData, function( error2, response2) { callback( error2, response2 ); return; }); } ); } exports.Upload_Overlay = function( device, filename, options, callback ) { if(!filename || typeof filename !== "string" ) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Must set filename for overlay" }); return; } var paths = filename.split("/"); var file = paths[paths.length-1]; VapixDigest.upload( device, "overlay", file, options, filename, function( error, response) { callback( error, response ); }); } exports.Upload_ACAP = function( device , filename, callback ) { if( typeof filename !== "string" || filename.length < 0 ) { msg.payload = { statusCode: 400, statusMessage: "Invalid input", body: "Filename neds to be set" } node.error(msg); return; } VapixDigest.upload( device, "acap", "acap.eap", null, filename, function( error, response) { if(!error) { //VAPIX responds error with 200 OK but error is in JSON try { response = JSON.parse(response); } catch { } if( response.hasOwnProperty("error") ) { callback( true, { statusCode: response.error.code || 0, statusMessage: response.error.message || "Unknown error", body: "Not compatible with device" }); return; } callback( false, response ); return; } VapixDigest.upload( device, "acap_legacy", "acap.eap", null, filename, function( error, response) { if( error ) { callback( error, response ); return; } var body = response.trim(); switch( body ) { case "OK": callback( false, "ACAP installed" ); break; case "Error: 1": callback(true,{ statusCode: 500, statusMessage: "Installation failed", body: body }); break; case "Error: 2": callback(true,{ statusCode: 500, statusMessage: "Installation failed", body: "File verification failed" }); break; case "Error: 3": callback(true,{ statusCode: 500, statusMessage: "Installation failed", body: "File is too large or the storage is full" }); break; case "Error: 5": case "Error: 10": callback(true,{ statusCode: 500, statusMessage: "Installation failed", body: "File is not compatible with the HW or FW" }); break; default: callback(true,{ statusCode: 500, statusMessage: "Installation failed", body: body }); break; } }); }); } exports.Certificates_Get = function( device, certificateID, callback ) { var body = '<tds:GetCertificateInformation xmlns="http://www.onvif.org/ver10/device/wsdl">'; body += '<CertificateID>' + certificateID + '</CertificateID>'; body += '</tds:GetCertificateInformation>'; VapixDigest.Soap( device, body, function( error, response ) { if( error ) { callback( error, response); return; } VapixParser.Certificate( response, function( error, cert ) { callback( error,cert); }); }); }; exports.Certificates_List = function( device, callback ){ var body = '<tds:GetCertificates xmlns="http://www.onvif.org/ver10/device/wsdl"></tds:GetCertificates>'; VapixDigest.Soap( device, body, function( error, response ) { if( error ) { callback(error, response); return; } VapixParser.Certificates( response, function( error, list ) { if( error ) { callback( error, response ); return; } if( list.length === 0 ) { callback( false, list ); return; } var certCounter = list.length; var theCallback = callback; var certList = []; for( var i = 0; i < list.length; i++ ) { exports.Certificates_Get( device, list[i].id, function( error, response ) { if( !error ) certList.push( response ); certCounter--; if( certCounter <= 0 ) theCallback( false, certList ); }); }; return; }); }); } exports.Certificates_CSR = function( device, options, callback){ if(!options || typeof options === "number" || typeof options === "boolean") { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Missing CSR" }); return; } csr = options; if( typeof csr === "string" ) csr = JSON.parse(csr); if(!csr) { callback(true,{ statusCode: 400, statusMessage: "Invalid input", body: "Invalid CSR JSON" }); return; } csr.id = "CSR_" + new Date().getTime(); VapixParser.CSR_Request_Body( csr, function( error, body ) { if( error ) { callback( error, body ); return; }; VapixDigest.Soap( device, body, function( error, response ) { if( error ) { callback(error,response); return; } VapixParser.SoapParser( response, function( error, data ) { if( !data.hasOwnProperty("acertificates:CreateCertificate2Response") ) { callback("Invalid request", data); return; } callback(error,{ id: data["acertificates:CreateCertificate2Response"]["acertificates:Id"], pem: data["acertificates:CreateCertificate2Response"]["acertificates:Certificate"] }); }); }); }); } exports.Recordings = function( device, options, callback) { var cgi = "/axis-cgi/record/list.cgi?recordingid=all"; if( options.hasOwnProperty("from") ) { cgi += "&starttime=" + new Date(options.from).toISOString(); if( options.hasOwnProperty("to") ) cgi += "&stoptime=" + new Date(options.to).toISOString(); } exports.CGI( device, cgi, function( error, response ) { if( error ) { callback(error, response ); return; } VapixParser.Recordings( response, function( error, body ) { callback(error, body); }); }); }