First commit

This commit is contained in:
Frank de Lange 2021-04-09 15:19:04 +02:00
parent 8f5243d474
commit c6e8eeacd5
7 changed files with 880 additions and 3 deletions

171
README.md
View file

@ -1,5 +1,172 @@
# zmapi # zmapi
ZoneMinder API CLI 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
Includes camera control (cam) and watchdog (camwatch)

494
cam Executable file
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
# example values only
username="orwell"
password="panopticon"
server="https://watchmen.example.org"