Initial commit

This commit is contained in:
Frank de Lange 2021-03-14 21:59:56 +01:00
parent c50b33b5b9
commit 9796e0fae9
2 changed files with 467 additions and 1 deletions

View file

@ -1,3 +1,13 @@
# cam
A simple script to control Xiongmai and related cameras using the anyka_ipc command daemon and "NETSDK" interface. It might work on others as well, it might not work at all. It works for me, mostly.
Cam controls cameras, or is supposed to. It is supposed to be more useable than the abominable web interface these things tend to come with. It is a simple script to control Xiongmai and related cameras using the `anyka_ipc` command daemon and `NETSDK` interface. It might work on others as well, it might not work at all. It works for me, mostly.
Use: `cam <camera_number|'all'> <command> [parameters]`
# configuration
You will need to edit the script to configure your camera IP addresses. If you changed the username and password on the device you can enter these as well but it is by no means guaranteed to work since it is quite probably that username `admin` and a blank password is hardcoded somewhere in the firmware on your camera.
Run the script without any parameters to see the available commands. Some commands take parameters, use `can 1 [command] -H` to see the available parameters and the accepted value ranges.
This script is based on what I could discern from a firmware dump parts of which I ran through Ghidra.

456
cam Executable file
View file

@ -0,0 +1,456 @@
#!/bin/bash
shopt -s extglob
declare -A camera=(
[1]=192.168.5.10
[2]=192.168.5.11
[3]=192.168.5.12
[4]=192.168.5.13
[5]=192.168.5.14
[6]=192.168.5.15
[7]=192.168.5.16
[8]=192.168.5.17
)
camlst=$(echo -n "${!camera[@]}"|tr ' ' '|')
username="admin"
password=""
auth=$(echo -n "$username:$password"|base64)
viewer="display"
player="mpv"
headers="-H 'Authorization: Basic $auth'"
# headers="-H 'Authorization: Basic $auth' -H 'Cookie: juanipcam_lang=en; login=${username}%2C${password}; sync_time=true; usr=$username; pwd=$password'"
declare -A SDK=(
[audio_encode]=/NetSDK/Audio/encode/channel/101
[audio_input]=/NetSDK/Audio/input/channel/1
[image]=/NetSDK/Image
[image_ircut]=/NetSDK/Image/irCutFilter
[image_denoise3d]=/NetSDK/Image/denoise3d
[image_sharpness]=/NetSDK/Image/manualSharpness
[image_videomode]=/NetSDK/Image/videoMode/properties/
[image_wdr]=/NetSDK/Image/wdr
[network_dns]=/NetSDK/Network/DNS
[network_esee]=/NetSDK/Network/ESee
[network_lan1]=/NetSDK/Network/interface/1/lan
[network_lan4]=/NetSDK/Network/interface/1/lan
[network_wifi]=/NetSDK/Network/interface/4/wireless
[network_port]=/NetSDK/Network/port
[wifi_allstainfo]=/NetSDK/Network/wireless/allStaInfo
[wifi_stationsignal]=/NetSDK/Network/wireless/stationSignal
[wifi_status]=/NetSDK/Network/Wireless/status
[ptz]=/NetSDK/PTZ/channel/1/control
[devinfo]=/NetSDK/System/deviceInfo
[reboot]=/NetSDK/System/operation/reboot
[time]=/NetSDK/System/time/localtime
[time_ntp]=/NetSDK/System/time/ntp
[time_rtc]=/NetSDK/System/time/rtc
[time_zone]=/NetSDK/System/time/timeZone
[time_calender]=/NetSDK/System/time/calendarStyle
[vencode_main]=/NetSDK/Video/encode/channel/101
[vencode_sub]=/NetSDK/Video/encode/channel/102
[video_channeloverlay]=/NetSDK/Video/encode/channel/101/channelNameOverlay
[video_extraoverlay]=/NetSDK/Video/encode/channel/101
[video_timeoverlay]=/NetSDK/Video/encode/channel/101/datetimeOverlay
[video_input]=/NetSDK/Video/input/channel/1
[video_brightness]=/NetSDK/Video/input/channel/1/brightnessLevel
[video_contrast]=/NetSDK/Video/input/channel/1/contrastLevel
[video_flip]=/NetSDK/Video/input/channel/1/flipEnabled
[video_mirror]=/NetSDK/Video/input/channel/1/mirrorEnabled
[video_hue]=/NetSDK/Video/input/channel/1/hueLevel
[video_mask]=/NetSDK/Video/input/channel/1/PrivacyMasks
[video_saturation]=/NetSDK/Video/input/channel/1/saturationLevel
[video_sharpness]=/NetSDK/Video/input/channel/1/sharpnessLevel
[motiondetection]=/NetSDK/Video/motionDetection/channel/1
[load]="/_SDK/info/monitor.php?nonce=123"
[ps]="/_SDK/info/monitor.php?nonce=123"
[debuginfo]="/_SDK/info/device.php?nonce=123"
[eventinfo]="/_SDK/info/event.php?nonce=123"
[health]=/Anyka/health
[config]=/Anyka/config
[log]=/Anyka/log
[oem]=/custom/OEM
[view_main]="/ch0_0.264"
[view_sub]="/ch0_1.264"
[snapshot]=/snapshot
[user_list]="/user/user_list.xml?username=$username&password=$password"
)
declare -A audio_encode=(e enabled:b i audioInputChannelID:i c codecType:s)
declare -A audio_input=(w workMode:s m microphoneType:s:auto,activePickup,passiveMic r sampleRate:i w sampleBitWidth:i i inputVolume:i o outputVolume:i)
declare -A image_ircut=(c irCutControlMode:s:hardware,software m irCutMode:s:auto,daylight,night)
declare -A image_denoise3d=(e enabled:b s denoise3dStrength:i)
declare -A image_sharpness=(e enabled:b -s sharpnessLevel:i)
declare -A image_wdr=(e enabled:b -s WDRStrength:i)
declare -A image=(s imageStyle:i:1,2,3 l lowlightMode:s:close,day-night,only-night,auto c sceneMode:s:auto,indoor,outdoor e exposureMode:s:auto,bright,dark a awbMode:s:auto,indoor,outdoor b BLcompensationMode:s)
declare -A image_videomode=(s imageStyle:i:1,2,3 l lowlightMode:s:close,day-night,only-night,auto c sceneMode:s:auto,indoor,outdoor e exposureMode:s:auto,bright,dark a awbMode:s:auto,indoor,outdoor b BLcompensationMode:s)
declare -A network_dns=(p preferredDns:s a staticAlternateDns:s)
declare -A network_esee=(e enabled:b)
declare -A network_lan1=(v ipVersion:s i staticIP:s n staticNetmask:s g staticGateway:s t addressingType:s o OnvifAutoAdapt:b d dhcp:b)
declare -A network_lan4=(v ipVersion:s i staticIP:s n staticNetmask:s g staticGateway:s t addressingType:s o OnvifAutoAdapt:b d dhcp:b)
declare -A network_wifi=(m wirelessMode:s s stationMode.wirelessStaMode:s b stationMode.wirelessApBssId:s a stationMode.wirelessApEssId:s p stationMode.wirelessApPsk:s)
declare -A network_port=(n portname:s p value:i)
declare -A reboot=()
declare -A time_ntp=(e enabled:b s ntpServerDomain:s)
declare -A time_zone=()
declare -A time=()
declare -A vencode_main=(e enabled:b r resolution:s c codecType:s b constantBitrate:i f frameRate:i t bitRateControlType:s p h264Profile:s k keyFrameInterval:i n channelName:s F freeResolution:b )
declare -A vencode_sub=(e enabled:b r resolution:s c codecType:s b constantBitrate:i f frameRate:i t bitRateControlType:s p h264Profile:s k keyFrameInterval:i n channelName:s F freeResolution:b )
declare -A video_timeoverlay=(e enabled:b x regionX:i y regionY:i d dateFormat:s t timeFormat:i w displayWeek:b c displayChinese:b)
declare -A video_channeloverlay=(e enabled:b x regionX:i y regionY:i)
declare -A video_extraoverlay=(e expandChannelNameOverlay[0].enabled:b t expandChannelNameOverlay[0].expandChannelName:s)
declare -A video_input=(e enabled:b l powerLineFrequencyMode:i:50,60,100,120 b brightnessLevel:i:0-100 c contrastLevel:i:0-100 s sharpnessLevel:i:0-100 a saturationLevel:i:0-100 h hueLevel:i:0-100 f flipEnabled:b m mirrorEnabled:b p privacyMask)
declare -A video_flip=()
declare -A video_mirror=()
declare -A video_brightness=()
declare -A video_contrast=()
declare -A video_saturation=()
declare -A video_hue=()
declare -A video_sharpness=()
is_json () {
jq -e . >/dev/null 2>&1 <<<"$@"
}
cam_url () {
cam="$1"
echo -n "http://${username}:${password}@${camera[$cam]}"
}
get () {
cam="$1"
cmd="$2"
curl -s "$headers" "$(cam_url $cam)${SDK[$cmd]}"
}
put () {
cam="$1"
cmd="$2"
data="$3"
curl -g -s "$headers" "$(cam_url $cam)${SDK[$cmd]}" -X PUT --data-raw "$(echo "$data")"
}
get_json () {
cam="$1"
command="$2"
jqq="$3"
if [ -z "$jqq" ]; then
jqq="."
fi
echo -n "$command CAM$cam (${camera[$cam]}): "
result=$(get "$cam" "$command")
if is_json "$result"; then
jq "$jqq"<<<"$result"
else
echo "$result"
fi
}
put_json () {
cam="$1"
command="$2"
data="$3"
jqq="$4"
if [ -z "$jqq" ]; then
jqq=".statusMessage"
fi
echo -n "$command CAM$cam (${camera[$cam]}): "
put "${cam}" "$command" "$data"|jq "$jqq"
}
get_text () {
cam="$1"
echo -n "$command CAM$cam (${camera[$cam]}):"
get "$cam" "$command"|tr -cd "[:print:]\n\t"
echo
}
check_range () {
value="$1"
range=$(echo "$2"|tr ',' '|')
field="$3"
if [[ "$range" == "$2" ]]; then
min=$(echo "$2"|cut -d '-' -f 1)
max=$(echo "$2"|cut -d '-' -f 2)
if [[ "$min" == "$max" ]]; then
printf "$field: invalid range specification\n\n"
exit 1
else
if [[ $value -ge $min && $value -le $max ]]; then
return
fi
fi
else
if [[ "$value" == @($range) ]]; then
return
fi
fi
printf "$field: value '$value' invalid\n\n"
printf "allowed values: $2\n"
exit 1
}
mutate () {
data="$1"
fieldtype="$2"
value="$3"
field=$(echo "$fieldtype"|cut -d ':' -f 1)
type=$(echo "$fieldtype"|cut -d ':' -f 2)
range=$(echo "$fieldtype"|cut -d ':' -f 3)
if [[ -n $range ]]; then
check_range "$value" "$range" "$field"
fi
case $type in
b|i)
jq ".$field=$value" <<<"$data"
;;
s)
jq ".$field=\"$value\"" <<<"$data"
;;
*)
jq ".$field=\"$value\"" <<<"$data"
;;
esac
}
help_command () {
command="$1"
declare -n opts="$command"
if [[ ${#opts[@]} -lt 1 ]]; then
printf "The $command command takes a single parameter and has no options\n"
else
printf "Available parameters for $command:\n\n"
for n in ${!opts[@]}; do printf "\t-$n\t$(echo "${opts[$n]}"| sed 's/:/ (type /;s/:/ values:/;s/$/\)/')\n";done
fi
echo
exit
}
config () {
cam="$1"
shift
command="$1"
shift
cam_ip="${camera[$cam]}"
declare -n opts="$command"
optstr="$(tr ' ' ':'<<<"${!opts[@]}"):H"
optlst="$(echo "${!opts[@]}"|sed -e 's/ /|/g')"
cc=$(get "$cam" "$command")
local OPTIND
while getopts "$optstr" OPTION; do
optset=1
case $OPTION in
H)
help_command $command
;;
@($optlst))
cc="$(mutate "$cc" "${opts[$OPTION]}" "$OPTARG")" || exit_with_error "$cc"
;;
*)
echo "unknown option $OPTION"
exit
;;
esac
done
if [ -v optset ];then
put_json "$cam" "$command" "$cc"
elif (declare -p $opts 2>&1>/dev/null) && [[ -z $optlst ]] && [[ -n $1 ]]; then
put_json "$cam" "$command" "\"$1\""
else
get_json "$cam" "$command"
fi
}
view () {
cam="$1"
cmd="$2"
$viewer "$(cam_url $cam)${SDK[$cmd]}" &
}
watch () {
cam="$1"
cmd="$2"
$player "rtsp://$username:$password@${camera[$cam]}${SDK[$cmd]}" &
}
help () {
printf "Use: $(basename $0) command [camera|'all']\n\n"
printf "available commands:\n\n"
printf '%s\n' "${!SDK[@]}"|sort
exit
}
exit_with_error () {
printf "ERROR: $@\n"
exit 1
}
# ---------------------------------------------------
case $1 in
@($camlst))
cam_s=$1
cam_f=$1
shift
;;
all)
cam_s=1
cam_f=8
shift
;;
*)
printf "camera number or 'all' missing\n\n"
help
esac
if [[ -z $1 ]]; then
help
else
command="$1"
shift
fi
if ! test ${SDK[$command]+_}; then
printf "unknown command $command\n\n"
help
fi
for cam in $(seq "$cam_s" "$cam_f");do
case "$command" in
load)
get_json "$cam" "$command" ".CPU.Usage"
;;
health|config|log)
get_text "$cam"
;;
reboot)
put_json "$cam" "$command" '{true}'
;;
snapshot)
view "$cam" "$command"
;;
view_main|view_sub)
watch "$cam" "$command"
;;
help)
help
;;
*)
config "$cam" "$command" "$@"
;;
esac
done
exit
# based on strings pulled from firmware dump, these represent api endpoints
# used with GET or PUT requests as defined in NK_N1Device_InitV2()
#
# check http://camera_ip/js/function.js (etc) for how these endpoints seem to expect
# to be fed amd cared for - "seem" because the firmware is quite buggy and not all
# endpoints seem to be connected.
#
# /NetSDK/
# /NetSDK/Audio/encode/channel
# /NetSDK/Audio/encode/channel/[id=101]
# /NetSDK/Audio/encode/channel/[id=101]/enabled
# /NetSDK/Audio/input/channel
# /NetSDK/Audio/input/channel/[id=1]
# /NetSDK/Audio/input/channel/[id=1]/inputVolume
# /NetSDK/Audio/input/channel/[id=1]/microphoneType
# /NetSDK/Audio/input/channel/[id=1]/outputVolume
# /NetSDK/Audio/input/channel/[id=1]/sampleBitWidth
# /NetSDK/Audio/input/channel/[id=1]/sampleRate
# /NetSDK/Factory
# /NetSDK/Image
# /NetSDK/Image/denoise3d
# /NetSDK/Image/irCutFilter
# /NetSDK/Image/manualSharpness
# /netsdk/image/videoMode
# /NetSDK/Image/videoMode
# /NetSDK/Image/wdr
# /NetSDK/Network/DNS
# /NetSDK/Network/ESee
# /NetSDK/Network/interface
# /NetSDK/Network/interface/[id=1]
# /NetSDK/Network/interface/[id=1]/lan
# /NetSDK/Network/interface/[id=4]
# /NetSDK/Network/interface/[id=4]/lan
# /NetSDK/Network/interface/[id=4]/wireless
# /NetSDK/Network/port
# /NetSDK/Network/port/[id=1]
# /NetSDK/Network/wireless/allStaInfo
# /NetSDK/Network/wireless/stationSignal
# /NetSDK/Network/Wireless/status
# /NetSDK/PTZ/channel/[id=1]/control
# /NetSDK/snapshot
# /NetSDK/snapshot.jpg
# /NetSDK/System/deviceInfo
# /NetSDK/System/deviceInfo/deviceAddress
# /NetSDK/System/deviceInfo/deviceName
# /NetSDK/System/operation
# /NetSDK/System/operation/default
# /NetSDK/System/operation/reboot
# /NetSDK/System/operation/remoteUpgrade
# /NetSDK/System/time
# /NetSDK/System/time/calendarStyle
# /NetSDK/System/time/localTime
# /NetSDK/System/time/ntp
# /NetSDK/System/time/rtc
# /NetSDK/System/time/timeZone
# /NetSDK/Video/encode/channel
# /NetSDK/Video/encode/channel/101/snapshot
# /NetSDK/Video/encode/channel/102/snapshot
# /NetSDK/Video/encode/channel/[id=101]
# /NetSDK/Video/encode/channel/[id=101]/channelNameOverlay
# /NetSDK/Video/encode/channel/[id=101]/datetimeOverlay
# /NetSDK/Video/encode/channel/[id=101]/requestKeyFrame
# /NetSDK/Video/encode/channel/[id=102]
# /NetSDK/Video/encode/channel/[id=102]/requestKeyFrame
# /NetSDK/Video/encode/channel/[id=1]/requestKeyFrame
# /NetSDK/Video/encode/channel/[id=2]/requestKeyFrame
# /NetSDK/Video/input/channel
# /NetSDK/Video/input/channel/[id=1]
# /NetSDK/Video/input/channel/[id=1]/brightnessLevel
# /NetSDK/Video/input/channel/[id=1]/contrastLevel
# /NetSDK/Video/input/channel/[id=1]/flipEnabled
# /NetSDK/Video/input/channel/[id=1]/hueLevel
# /NetSDK/Video/input/channel/[id=1]/mirrorEnabled
# /NetSDK/Video/input/channel/[id=1]/PrivacyMask
# /NetSDK/Video/input/channel/[id=1]/saturationLevel
# /NetSDK/Video/input/channel/[id=1]/sharpnessLevel
# /NetSDK/Video/motionDetection/channel
# /NetSDK/Video/motionDetection/channel/[id=1]
#
# /cgi-bin/gw2.cgi
# /cgi-bin/hi3510/echo.cgi
# /cgi-bin/hi3510/ptzctrl.cgi
# /cgi-bin/hi3510/preset.cgi
# /cgi-bin/hi3510/param.cgi
#
# /livestream/11
# /livestream/12
#
# /livestream/hinetate/11
# /livestream/hinetate/12
#
# /snapshot
# /snapshot.jpg
#
# /user/add_user.xml
# /user/del_user.xml
# /user/set_pass.xml
# /user/user_list.xml
#
# /_SDK/index.php
# /_SDK/info/device.php
# /_SDK/info/event.php
# /_SDK/info/license.php
# /_SDK/info/monitor.php
#