First commit
This commit is contained in:
parent
8f5243d474
commit
c6e8eeacd5
7 changed files with 880 additions and 3 deletions
173
README.md
173
README.md
|
@ -1,5 +1,172 @@
|
|||
# zmapi
|
||||
|
||||
ZoneMinder API CLI
|
||||
|
||||
Includes camera control (cam) and watchdog (camwatch)
|
||||
A simple, shell (bash) based ZoneMinder API CLI which, combined with a camera control (cam) and watchdog (camwatch) can be used to keep a ZoneMinder installation up and running without having to deal with those clunky web interfaces.
|
||||
|
||||
The ZoneMinder API CLI script (`zmapi`) is independent of the camera control scripts (`cam` and `camwatch`) and each can be used by itself. The camera-related scripts are specific for (XiongMai-based) cameras using the 'NetSDK' API and will need to be adapted to other camera types.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the scripts (`zmapi`, `cam` and `camwatch`) to a directory in the PATH. Either copy and edit the configuration files (`zmapi.conf`, `cam.conf` and `camwatch.conf`) to the `$HOME/.config/zmapi` directory for the user who will executre these scripts. If the configuration files are not present on first run the scripts will create them with default values on the first run and abort, in that case edit the created files before continuing.
|
||||
|
||||
Run `zmapi` and `cam` without parameters for an overview of the available commands, alternatively have a look at the source for more insight. The available commands are defined in the API (`zmapi`) or SDK (`cam`) arrays, this is also where new commands can be added if new API endpoints are discovered.
|
||||
|
||||
## Examples
|
||||
|
||||
### ZoneMinder
|
||||
|
||||
Show all monitors:
|
||||
|
||||
```$ zmapi monitors
|
||||
...
|
||||
...lots of JSON output...```
|
||||
|
||||
Show monitor #1:
|
||||
|
||||
```$ zmapi monitor 1
|
||||
...
|
||||
...lots of JSON output...```
|
||||
|
||||
Show monitor #1 status:
|
||||
|
||||
```$ zmapi status 1
|
||||
{
|
||||
"MonitorId": "1",
|
||||
"Status": "Connected",
|
||||
"CaptureFPS": "10.00",
|
||||
"AnalysisFPS": "10.00",
|
||||
"CaptureBandwidth": "234455"
|
||||
}
|
||||
```
|
||||
|
||||
Show daemon status for monitor #1 (default shows `zmc` daemon status):
|
||||
|
||||
```$ zmapi daemonstatus 1
|
||||
{
|
||||
"status": true,
|
||||
"statustext": "'zmc -m 1' running since 21/04/09 01:06:46, pid = 12303"
|
||||
}```
|
||||
|
||||
Show `zma` status for that monitor:
|
||||
|
||||
```$ zmapi daemonstatus 1 zma
|
||||
{
|
||||
"status": true,
|
||||
"statustext": "'zma -m 1' running since 21/04/09 01:06:52, pid = 12314"
|
||||
}```
|
||||
|
||||
...etc
|
||||
|
||||
### Camera control
|
||||
|
||||
Show the time according to camera number 1:
|
||||
|
||||
```$ cam 1 time
|
||||
time CAM1 (192.168.5.10): "2021-04-09T15:02:48+02:00"```
|
||||
|
||||
Set the time on all cameras to the current time:
|
||||
|
||||
```$ cam all time $(date -Iseconds)
|
||||
$ cam all time $(date -Iseconds)
|
||||
time CAM1 (192.168.5.10): "OK"
|
||||
time CAM2 (192.168.5.11): "OK"
|
||||
time CAM3 (192.168.5.12): "OK"
|
||||
time CAM4 (192.168.5.13): "OK"
|
||||
time CAM5 (192.168.5.14): "OK"
|
||||
time CAM6 (192.168.5.15): "OK"
|
||||
time CAM7 (192.168.5.16): "OK"```
|
||||
|
||||
Reboot camera #7:
|
||||
|
||||
```$ cam 7 reboot
|
||||
reboot CAM7 192.168.5.16): "OK"```
|
||||
|
||||
Show video encoding configuration for camera #1:
|
||||
|
||||
```$ cam 1 vencode_main
|
||||
vencode_main CAM1 (192.168.5.10): {
|
||||
"id": 101,
|
||||
"enabled": true,
|
||||
"videoInputChannelID": 101,
|
||||
"codecType": "H.264",
|
||||
"h264Profile": "high",
|
||||
"freeResolution": false,
|
||||
"channelName": "CAM1",
|
||||
"bitRateControlType": "VBR",
|
||||
"resolution": "1920x1080",
|
||||
"constantBitRate": 2048,
|
||||
"frameRate": 10,
|
||||
"keyFrameInterval": 50,
|
||||
"expandChannelNameOverlay": [
|
||||
{
|
||||
"expandChannelName": "cam1",
|
||||
"id": 1,
|
||||
"enabled": false,
|
||||
"regionX": 0,
|
||||
"regionY": 0
|
||||
},
|
||||
{
|
||||
"expandChannelName": "",
|
||||
"id": 2,
|
||||
"enabled": false,
|
||||
"regionX": 0,
|
||||
"regionY": 0
|
||||
},
|
||||
{
|
||||
"expandChannelName": "",
|
||||
"id": 3,
|
||||
"enabled": false,
|
||||
"regionX": 0,
|
||||
"regionY": 0
|
||||
},
|
||||
{
|
||||
"expandChannelName": "",
|
||||
"id": 4,
|
||||
"enabled": false,
|
||||
"regionX": 0,
|
||||
"regionY": 0
|
||||
}
|
||||
],
|
||||
"channelNameOverlay": {
|
||||
"enabled": true,
|
||||
"regionX": 0,
|
||||
"regionY": 0
|
||||
},
|
||||
"datetimeOverlay": {
|
||||
"enabled": true,
|
||||
"regionX": 1,
|
||||
"regionY": 1,
|
||||
"dateFormat": "YYYY/MM/DD",
|
||||
"timeFormat": 24,
|
||||
"displayWeek": true,
|
||||
"displayChinese": false
|
||||
},
|
||||
"deviceIDOverlay": {
|
||||
"enabled": false,
|
||||
"regionX": 0,
|
||||
"regionY": 0
|
||||
},
|
||||
"textOverlays": ""
|
||||
}```
|
||||
|
||||
Show which video encoding parameters can be changed on camera #1:
|
||||
``` cam 1 vencode_main -H
|
||||
Available parameters for vencode_main:
|
||||
|
||||
-F freeResolution (type b)
|
||||
-t bitRateControlType (type s)
|
||||
-r resolution (type s)
|
||||
-p h264Profile (type s)
|
||||
-n channelName (type s)
|
||||
-k keyFrameInterval (type i)
|
||||
-f frameRate (type i)
|
||||
-e enabled (type b)
|
||||
-c codecType (type s)
|
||||
-b constantBitrate (type i)```
|
||||
|
||||
Set the channelName (the name shown in the bottom-right of the image) for camera #2 to *WATCHTOWER*:
|
||||
|
||||
``` cam 2 vencode_main -n WATCHTOWER
|
||||
vencode_main CAM2 (192.168.5.11): "OK"```
|
||||
|
||||
...etc
|
||||
|
||||
|
|
494
cam
Executable file
494
cam
Executable file
|
@ -0,0 +1,494 @@
|
|||
#!/bin/bash
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
declare -A camera
|
||||
|
||||
username="admin"
|
||||
password=""
|
||||
viewer="display"
|
||||
player="mpv"
|
||||
|
||||
confdir="$HOME/.config/zmapi"
|
||||
config="cam.conf"
|
||||
|
||||
if [ -f "$confdir/$config" ]; then
|
||||
source "$confdir/$config"
|
||||
else
|
||||
echo "configuration file ($confdir/$config) missing"
|
||||
echo "new file created, edit it and restart program"
|
||||
echo <<-EOT > "$confdir/$config"
|
||||
camera=()
|
||||
username=
|
||||
password=
|
||||
viewer=
|
||||
player=
|
||||
EOT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
auth=$(echo -n "$username:$password"|base64)
|
||||
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'"
|
||||
|
||||
camlst=$(echo -n "${!camera[@]}"|tr ' ' '|')
|
||||
|
||||
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]}" &
|
||||
}
|
||||
|
||||
is_ip () {
|
||||
if [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
|
||||
then
|
||||
#echo "IP is $1"
|
||||
ret=0
|
||||
else
|
||||
#echo "$1 is not IP"
|
||||
ret=1
|
||||
fi
|
||||
return $ret
|
||||
}
|
||||
|
||||
cam_for_ip () {
|
||||
for cam in ${!camera[@]}; do
|
||||
if [[ "${camera[$cam]}" == "$1" ]]; then
|
||||
echo -n $cam
|
||||
return
|
||||
fi
|
||||
done
|
||||
exit_with_error "$1: unknown camera IP"
|
||||
}
|
||||
|
||||
help () {
|
||||
printf "Use: $(basename $0) command [camera|IP address|'all']\n\n"
|
||||
printf "available commands:\n\n"
|
||||
printf '%s\n' "${!SDK[@]}"|sort
|
||||
exit
|
||||
}
|
||||
|
||||
exit_with_error () {
|
||||
printf "ERROR: $@\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------
|
||||
|
||||
if is_ip "$1"; then
|
||||
cam=$(cam_for_ip "$1")
|
||||
parameters=( "$@" )
|
||||
parameters[0]=$cam
|
||||
set -- "${parameters[@]}"
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
|
||||
@($camlst))
|
||||
cam_s=$1
|
||||
cam_f=$1
|
||||
shift
|
||||
;;
|
||||
all)
|
||||
cam_s=1
|
||||
cam_f=8
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
printf "camera number, IP address 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
|
||||
#
|
16
cam.conf
Normal file
16
cam.conf
Normal file
|
@ -0,0 +1,16 @@
|
|||
# example values only
|
||||
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
|
||||
)
|
||||
|
||||
username="admin"
|
||||
password=""
|
||||
viewer="display"
|
||||
player="mpv"
|
57
camwatch
Executable file
57
camwatch
Executable file
|
@ -0,0 +1,57 @@
|
|||
#!/bin/bash
|
||||
|
||||
declare -a monitors
|
||||
threshold=3
|
||||
|
||||
confdir="$HOME/.config/zmapi"
|
||||
config="camwatch.conf"
|
||||
|
||||
mon_stat_store="$confdir/mon_stat_store"
|
||||
|
||||
|
||||
if [ -f "$confdir/$config" ]; then
|
||||
source "$confdir/$config"
|
||||
else
|
||||
echo "configuration file ($confdir/$config) missing"
|
||||
echo "new file created, edit it and restart program"
|
||||
echo <<-EOT > "$confdir/$config"
|
||||
monitors=()
|
||||
threshold=3
|
||||
mon_stat_store="$confdir/mon_stat_store"
|
||||
EOT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare -A mon_fps
|
||||
declare -A mon_stat
|
||||
declare -A cam_reboot
|
||||
|
||||
[ -f $mon_stat_store ] && source $mon_stat_store
|
||||
|
||||
if ! declare -p mon_stat 2>&1>/dev/null;then declare -A mon_stat;fi
|
||||
|
||||
while read mon fps; do
|
||||
mon_fps["$mon"]="$fps"
|
||||
done <<<"$(zmapi monitors|jq -r '.[][]["Monitor_Status"]|[.MonitorId,.CaptureFPS] |join(" ")')"
|
||||
|
||||
for mon in ${monitors[*]};do
|
||||
if [[ ${mon_fps[$mon]} == "0.00" ]]; then
|
||||
echo "$mon not capturing"
|
||||
mon_stat[$mon]=$((${mon_stat[$mon]}+1))
|
||||
if [ ${mon_stat[$mon]} -gt $threshold ]; then
|
||||
echo "Monitor $mon above threshold"
|
||||
cam_ip=$(zmapi monitor $mon|jq '.[].Monitor.Path'|grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
|
||||
cam_reboot[$cam_ip]=1
|
||||
mon_stat[$mon]=0
|
||||
fi
|
||||
else
|
||||
mon_stat[$mon]=0
|
||||
fi
|
||||
done
|
||||
|
||||
declare -p mon_stat > $mon_stat_store
|
||||
|
||||
for ip in ${!cam_reboot[*]};do
|
||||
echo "rebooting cam $ip..."
|
||||
cam "$ip" reboot
|
||||
done
|
4
camwatch.conf
Normal file
4
camwatch.conf
Normal file
|
@ -0,0 +1,4 @@
|
|||
# example values only
|
||||
monitors=(1 2 4 5 6 7 8 9 10 11 12 13 14 15 3 16)
|
||||
threshold=3
|
||||
mon_stat_store="/home/orwell/.config/zmapi/mon_stat_store"
|
135
zmapi
Executable file
135
zmapi
Executable file
|
@ -0,0 +1,135 @@
|
|||
#!/bin/bash
|
||||
|
||||
username=""
|
||||
password=""
|
||||
server=""
|
||||
|
||||
confdir="$HOME/.config/zmapi"
|
||||
config="zmapi.conf"
|
||||
access_token="access.json"
|
||||
refresh_token="refresh.json"
|
||||
|
||||
if [ -f "$confdir/$config" ]; then
|
||||
source "$confdir/$config"
|
||||
else
|
||||
echo "configuration file ($confdir/$config) missing"
|
||||
echo "new file created, edit it and restart program"
|
||||
echo <<-EOT > "$confdir/$config"
|
||||
username=
|
||||
password=
|
||||
server=
|
||||
EOT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare -A API=(
|
||||
[monitors]='monitors.json|G'
|
||||
[sourcetypes]='monitors/sourceTypes.json|G'
|
||||
[monitor]='monitors/${1}.json|G'
|
||||
[daemonstatus]='monitors/daemonStatus/id:${1}/daemon:${2:-zmc}.json|G'
|
||||
[daemoncontrol]='monitors/daemonStatus/id:${1}/command:${2}/daemon:${3}.json|G'
|
||||
[status]='monitors/${1}.json|G|.monitor.Monitor_Status'
|
||||
[alarm]='monitors/alarm/id:${1}/command:${2:-status}.json|G'
|
||||
[login]='host/login.json|P'
|
||||
[events]='events/index/${1}.json|G'
|
||||
[console_events]='events/consoleEvents/${1}.json|G'
|
||||
[states]='states.json|G'
|
||||
[restart]='states/change/restart.json|P'
|
||||
[stop]='states/change/stop.json|P'
|
||||
[start]='states/change/start.json|P'
|
||||
[zones]='zones.json|G'
|
||||
[load]='host/getLoad.json|G'
|
||||
[daemoncheck]='host/daemonCheck.json|G'
|
||||
[getdiskpercent]='host/getDiskPercent.json|G'
|
||||
[storage]='storage.json|G'
|
||||
[servers]='servers.json|G'
|
||||
[configs]='configs.json|G'
|
||||
)
|
||||
|
||||
get () {
|
||||
cmd="$1"
|
||||
shift
|
||||
token="$1"
|
||||
shift
|
||||
path=$(echo "${API[$cmd]}"|cut -d '|' -f 1)
|
||||
filter=$(echo "${API[$cmd]}"|cut -d '|' -f 3)
|
||||
if [ -z "$filter" ]; then
|
||||
filter='.'
|
||||
fi
|
||||
if [ $# -gt 0 ]; then
|
||||
path="$(eval echo "$path")"
|
||||
fi
|
||||
(if [ $token == "null" ]; then
|
||||
curl -s -XGET "$server/api/${path}"
|
||||
else
|
||||
curl -s -XGET "$server/api/${path}?token=$token"
|
||||
fi)|jq -r "$filter"
|
||||
|
||||
}
|
||||
|
||||
post () {
|
||||
cmd="$1"
|
||||
shift
|
||||
token="$1"
|
||||
shift
|
||||
data="$*"
|
||||
path="${API[$cmd]%|P}"
|
||||
if [ $token == "null" ]; then
|
||||
curl -s -XPOST -d "$data" "$server/api/${path}"
|
||||
else
|
||||
curl -s -XPOST -d "$data" "$server/api/${path}?token=$token"
|
||||
fi
|
||||
}
|
||||
|
||||
get_token () {
|
||||
atoken_expires=110
|
||||
rtoken_expires=1400
|
||||
access=$(find "$confdir" -name "$access_token" -mmin -$atoken_expires)
|
||||
refresh=$(find "$confdir" -name "$refresh_token" -mmin -$rtoken_expires)
|
||||
if [[ -n "$access" ]]; then
|
||||
token=$(jq -r '.access_token' "$confdir/$access_token")
|
||||
else
|
||||
if [[ -n "$refresh" ]]; then
|
||||
rtoken=$(jq -r '.refresh_token' "$confdir/$refresh_token")
|
||||
token=$(post login $rtoken|tee "$confdir/$access_token"|jq -r '.access_token')
|
||||
else
|
||||
data="user=$username&pass=$password"
|
||||
token=$(post login null "$data" |tee "$confdir/$refresh_token"|jq -r '.access_token')
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -n $token
|
||||
}
|
||||
|
||||
help () {
|
||||
printf "Use: $(basename $0) command [[parameter1] [[parameter2...]]]\n\n"
|
||||
printf "available commands:\n\n"
|
||||
printf '%s\n' "${!API[@]}"|sort
|
||||
exit
|
||||
}
|
||||
|
||||
|
||||
token=$(get_token)
|
||||
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
if ! test ${API[$cmd]+_}; then
|
||||
printf "unknown command $cmd\n\n"
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
method=$(echo "${API[$cmd]}"|cut -d '|' -f 2)
|
||||
|
||||
case "$method" in
|
||||
G)
|
||||
get "$cmd" "$token" "$@"
|
||||
;;
|
||||
P)
|
||||
post "$cmd" "$token" "$@"
|
||||
;;
|
||||
*)
|
||||
printf "unknown method $method for command $cmd"
|
||||
;;
|
||||
esac
|
4
zmapi.conf
Normal file
4
zmapi.conf
Normal file
|
@ -0,0 +1,4 @@
|
|||
# example values only
|
||||
username="orwell"
|
||||
password="panopticon"
|
||||
server="https://watchmen.example.org"
|
Loading…
Add table
Add a link
Reference in a new issue