1
0
mirror of https://github.com/EliasKotlyar/Xiaomi-Dafang-Hacks.git synced 2025-09-03 21:22:40 +02:00

Merge beta into master (#1786)

This commit is contained in:
timstanley1985
2021-10-24 09:57:50 +01:00
committed by GitHub
parent d2fa6d7aac
commit f1cf7dad8b
21 changed files with 884 additions and 141 deletions

View File

@@ -1,18 +1,54 @@
#!/bin/sh #!/bin/sh
CURL="/system/sdcard/bin/curl --silent"
JQ="/system/sdcard/bin/jq"
what="$1" what="$1"
shift
data="$@" if [ "$what" = "m" ]; then
CURL="/system/sdcard/bin/curl" shift
sendtext="${@//\"/\\\"}"
else
filename="$2"
datafile="$3"
fi
. /system/sdcard/config/matrix.conf . /system/sdcard/config/matrix.conf
sendMessage() { uploadData() {
text="$(echo "${@}" | sed 's:\\n:\n:g')" mimetype="$1"
echo "Sending message: $text" msgtype="$2"
$CURL -XPOST -d '{"msgtype":"m.text", "body":"'$text'"}' "https://$host:$port/_matrix/client/r0/rooms/$room_id/send/m.room.message?access_token=$access_token" mxcuri="$($CURL -XPOST -H "Content-Type: $mimetype" --data-binary @"$datafile" "https://$host:$port/_matrix/media/r0/upload?filename=$filename&access_token=$access_token" | $JQ -cMr ".content_uri")"
if [ -n "$mxcuri" ] && [ "$mxcuri" != "null" ]; then
$CURL -XPOST -d '{"msgtype":"'"$msgtype"'", "body":"'"$filename"'", "url":"'"$mxcuri"'"}' "https://$host:$port/_matrix/client/r0/rooms/$room_id/send/m.room.message?access_token=$access_token"
fi
} }
[ "$what" = "m" ] && sendMessage $data sendFile() {
[ -z "$what" ] && echo -e "$0 <m|f|p> <data>\n m: message\n f: file\n p: picture" echo "Sending file: $datafile"
uploadData "application/octet-stream" "m.file"
}
sendMessage() {
echo "Sending message: $sendtext"
$CURL -XPOST -d '{"msgtype":"m.notice", "body":"'"$sendtext"'"}' "https://$host:$port/_matrix/client/r0/rooms/$room_id/send/m.room.message?access_token=$access_token"
}
sendPicture() {
echo "Sending picture: $datafile"
filename="$filename.jpg"
uploadData "image/jpeg" "m.image"
}
sendVideo() {
echo "Sending video: $datafile"
filename="$filename.mp4"
uploadData "video/mp4" "m.video"
}
[ "$what" = "f" ] && sendFile
[ "$what" = "m" ] && sendMessage
[ "$what" = "p" ] && sendPicture
[ "$what" = "v" ] && sendVideo
[ -z "$what" ] && echo -e "$0 <m|f|p|v> <data|filename>\n m: message\n f: file\n p: picture\n v: video"

View File

@@ -18,45 +18,66 @@ sendMessage() {
} }
sendFile() { sendFile() {
echo "Sending file: $1" if [ -r $1 ]
$CURL -s \ then
-X POST \ echo "Sending file: $1"
https://api.telegram.org/bot$apiToken/sendDocument \ $CURL -s \
-F chat_id="$userChatId" \ -X POST \
-F document=@"$1" https://api.telegram.org/bot$apiToken/sendDocument \
-F chat_id="$userChatId" \
-F document=@"$1"
else
echo "File not found: $1"
fi
} }
sendPhoto() { sendPhoto() {
caption="$(hostname)-$(date +"%d%m%Y_%H%M%S")" if [ -r $1 ]
echo "Sending Photo: $1 $caption" >> /tmp/telegram.log then
$CURL -s \ caption="$(hostname)-$(date +"%d%m%Y_%H%M%S")"
-X POST \ echo "Sending Photo: $1 $caption" >> /tmp/telegram.log
https://api.telegram.org/bot$apiToken/sendPhoto \ $CURL -s \
-F chat_id="$userChatId" \ -X POST \
-F photo="@${1}" \ https://api.telegram.org/bot$apiToken/sendPhoto \
-F caption="${caption}" -F chat_id="$userChatId" \
-F photo="@${1}" \
-F caption="${caption}"
else
echo "File not found: $1"
fi
} }
sendVideo() { sendVideo() {
caption="$(hostname)-$(date +"%d%m%Y_%H%M%S")" if [ -r $1 ]
echo "Sending Video: $1 $caption" >> /tmp/telegram.log then
$CURL -s \ bytes=$(busybox stat -c %s $1)
-X POST \ caption="$(hostname)-$(date +"%d%m%Y_%H%M%S")"
https://api.telegram.org/bot$apiToken/sendVideo \ echo "Sending Video: $1 $caption (${bytes}bytes)" >> /tmp/telegram.log
-F chat_id="$userChatId" \ $CURL -s \
-F video="@${1}" \ -X POST \
-F caption="${caption}" https://api.telegram.org/bot$apiToken/sendVideo \
-F chat_id="$userChatId" \
-F video="@${1}" \
-F caption="${caption}"
else
echo "File not found: $1"
fi
} }
sendAnimation() { sendAnimation() {
caption="$(hostname)-$(date +"%d%m%Y_%H%M%S")" if [ -r $1 ]
echo "Sending Animation: $1 $caption" >> /tmp/telegram.log then
$CURL -s \ caption="$(hostname)-$(date +"%d%m%Y_%H%M%S")"
-X POST \ echo "Sending Animation: $1 $caption" >> /tmp/telegram.log
https://api.telegram.org/bot$apiToken/sendAnimation \ $CURL -s \
-F chat_id="$userChatId" \ -X POST \
-F animation="@${1}" \ https://api.telegram.org/bot$apiToken/sendAnimation \
-F caption="${caption}" -F chat_id="$userChatId" \
-F animation="@${1}" \
-F caption="${caption}"
else
echo "File not found: $1"
fi
} }
[ "$what" == "m" ] && sendMessage $data [ "$what" == "m" ] && sendMessage $data

View File

@@ -24,6 +24,7 @@ send_email=false
send_telegram=false send_telegram=false
telegram_alert_type=image telegram_alert_type=image
send_matrix=false send_matrix=false
matrix_alert_type=image
night_mode_event_delay=30 night_mode_event_delay=30
# General # General
@@ -63,6 +64,7 @@ ftp_videos_dir="motion/videos"
# Configure Dropbox snapshots and videos # Configure Dropbox snapshots and videos
dropbox_snapshot=false dropbox_snapshot=false
dropbox_video=false dropbox_video=false
dropbox_url="https://content.dropboxapi.com/2/files/upload"
dropbox_token="token" dropbox_token="token"
dropbox_stills_dir="/motion/stills" dropbox_stills_dir="/motion/stills"
dropbox_videos_dir="/motion/videos" dropbox_videos_dir="/motion/videos"

View File

@@ -49,7 +49,7 @@ FRAMERATE_NUM=25
VIDEOFORMAT=2 VIDEOFORMAT=2
# AudioFormat # AudioFormat
# Can be: OPUS | MP3 | PCM | PCMU # Can be: OPUS | MP3 | PCM | PCMU | OFF
AUDIOFORMAT=MP3 AUDIOFORMAT=MP3
# Audio sampling rate # Audio sampling rate
AUDIOINBR=16000 AUDIOINBR=16000

View File

@@ -40,24 +40,29 @@ send_snapshot() {
if [ "$telegram_alert_type" = "text" ] ; then if [ "$telegram_alert_type" = "text" ] ; then
debug_msg "Send telegram text" debug_msg "Send telegram text"
/system/sdcard/bin/telegram m "Motion detected" /system/sdcard/bin/telegram m "Motion detected"
elif [ "$telegram_alert_type" = "image" -o "$telegram_alert_type" = "video+image" ] ; then elif [ "$telegram_alert_type" = "image" -o "$telegram_alert_type" = "video+image" ] ; then
debug_msg "Send telegram image" debug_msg "Send telegram image"
/system/sdcard/bin/telegram p "$snapshot_tempfile" /system/sdcard/bin/telegram p "$snapshot_tempfile"
fi fi
) & ) &
fi fi
# Send a matrix message # Send a matrix message or image
if [ "$send_matrix" = true ]; then if [ "$send_matrix" = true ]; then
( (
include /system/sdcard/config/matrix.conf include /system/sdcard/config/matrix.conf
debug_msg "Send matrix message"
/system/sdcard/bin/matrix m "Motion detected" if [ "$matrix_alert_type" = "text" ] ; then
debug_msg "Send matrix text"
/system/sdcard/bin/matrix m "Motion detected"
elif [ "$matrix_alert_type" = "image" -o "$matrix_alert_type" = "video+image" ] ; then
debug_msg "Send matrix image"
/system/sdcard/bin/matrix p "$filename" "$snapshot_tempfile"
fi
) & ) &
fi fi
# Save FTP snapshot
#save FTP snapshot
if [ "$ftp_snapshot" = true ]; then if [ "$ftp_snapshot" = true ]; then
( (
ftpput_cmd="/system/sdcard/bin/busybox ftpput" ftpput_cmd="/system/sdcard/bin/busybox ftpput"
@@ -78,12 +83,11 @@ send_snapshot() {
) & ) &
fi fi
#save Dropbox snapshot # Save Dropbox snapshot
if [ "$dropbox_snapshot" = true ]; then if [ "$dropbox_snapshot" = true ]; then
( (
debug_msg "Sending Dropbox snapshot to $dropbox_stills_dir/$filename.jpg" debug_msg "Sending Dropbox snapshot to $dropbox_stills_dir/$filename.jpg"
/system/sdcard/bin/curl -X POST https://content.dropboxapi.com/2/files/upload \ /system/sdcard/bin/curl -X POST "$dropbox_url" \
--header "Authorization: Bearer $dropbox_token" \ --header "Authorization: Bearer $dropbox_token" \
--header "Dropbox-API-Arg: {\"path\": \"$dropbox_stills_dir/$filename.jpg\"}" \ --header "Dropbox-API-Arg: {\"path\": \"$dropbox_stills_dir/$filename.jpg\"}" \
--header "Content-Type: application/octet-stream" \ --header "Content-Type: application/octet-stream" \
@@ -91,17 +95,15 @@ send_snapshot() {
) & ) &
fi fi
# Save a snapshot
if [ "$save_snapshot" = true ] ; then
(
debug_msg "Save snapshot to $save_snapshot_dir/$groupname/$filename.jpg"
if [ ! -d "$save_snapshot_dir/$groupname" ]; then
# Save a snapshot mkdir -p "$save_snapshot_dir/$groupname"
if [ "$save_snapshot" = true ] ; then chmod "$save_dirs_attr" "$save_snapshot_dir/$groupname"
( fi
debug_msg "Save snapshot to $save_snapshot_dir/$groupname/$filename.jpg"
if [ ! -d "$save_snapshot_dir/$groupname" ]; then
mkdir -p "$save_snapshot_dir/$groupname"
chmod "$save_dirs_attr" "$save_snapshot_dir/$groupname"
fi
# Limit the number of snapshots # Limit the number of snapshots
if [ "$(ls "$save_snapshot_dir" | wc -l)" -ge "$max_snapshot_days" ]; then if [ "$(ls "$save_snapshot_dir" | wc -l)" -ge "$max_snapshot_days" ]; then
@@ -113,7 +115,7 @@ send_snapshot() {
) & ) &
fi fi
## Save SMB snapshot # Save SMB snapshot
if [ "$smb_snapshot" = true ]; then if [ "$smb_snapshot" = true ]; then
( (
smbclient_cmd="/system/bin/smbclient $smb_share" smbclient_cmd="/system/bin/smbclient $smb_share"
@@ -131,6 +133,7 @@ send_snapshot() {
$smbclient_cmd -D "$smb_stills_path" -c "lcd /tmp; mkdir $groupname; cd $groupname; put $snapshot_tempfilename; rename $snapshot_tempfilename $filename.jpg" $smbclient_cmd -D "$smb_stills_path" -c "lcd /tmp; mkdir $groupname; cd $groupname; put $snapshot_tempfilename; rename $snapshot_tempfilename $filename.jpg"
) & ) &
fi fi
# Wait for all background jobs to finish before existing # Wait for all background jobs to finish before existing
debug_msg "Waiting for background jobs to end in send_snapshot function:" debug_msg "Waiting for background jobs to end in send_snapshot function:"
for jobpid in $(jobs -p); do for jobpid in $(jobs -p); do
@@ -141,11 +144,11 @@ send_snapshot() {
} }
record_video () { record_video () {
# We only want one video stream at a time. Try to grab an # We only want one video stream at a time. Try to grab an exclusive
# exclusive flock on file descriptor 5. Bail out if another # flock on file descriptor 5. Bail out if another process already has
# process already has it. Touch the flock to update it's mod # it. Touch the flock to update its mod time as a signal to the
# time as a signal to the background process to keep recording # background process to keep recording when motion is repeatedly
# when motion is repeatedly observed. # observed.
touch /run/recording_video.flock touch /run/recording_video.flock
exec 5<> /run/recording_video.flock exec 5<> /run/recording_video.flock
if /system/sdcard/bin/busybox flock -n -x 5; then if /system/sdcard/bin/busybox flock -n -x 5; then
@@ -159,7 +162,6 @@ record_video () {
else else
/system/sdcard/bin/openRTSP -4 -w "$video_rtsp_w" -h "$video_rtsp_h" -f "$video_rtsp_f" -d "$video_duration" -b "$output_buffer_size" rtsp://$USERNAME:$USERPASSWORD@127.0.0.1:$PORT/unicast > "$video_tempfile" /system/sdcard/bin/openRTSP -4 -w "$video_rtsp_w" -h "$video_rtsp_h" -f "$video_rtsp_f" -d "$video_duration" -b "$output_buffer_size" rtsp://$USERNAME:$USERPASSWORD@127.0.0.1:$PORT/unicast > "$video_tempfile"
fi fi
else else
# Use avconv to stitch multiple JPEGs into 1fps video. # Use avconv to stitch multiple JPEGs into 1fps video.
# I couldn't get it working another way. # I couldn't get it working another way.
@@ -205,18 +207,23 @@ filename=$(date "$filename_pattern")
/system/sdcard/bin/getimage > "$snapshot_tempfile" /system/sdcard/bin/getimage > "$snapshot_tempfile"
debug_msg "Got snapshot_tempfile=$snapshot_tempfile" debug_msg "Got snapshot_tempfile=$snapshot_tempfile"
#Next send picture alerts in the background # Next send picture alerts in the background
send_snapshot & send_snapshot &
# Then, record video (if necessary) # Then, record video (if necessary)
if [ "$save_video" = true -o "$smb_video" = true -o "$telegram_alert_type" = "video+image" -o "$telegram_alert_type" = "video" -o "$publish_mqtt_video" = true ] ; then if [ "$save_video" = true ] ||
[ "$smb_video" = true ] ||
[ "$dropbox_video" = true ] ||
([ "$send_telegram" = true ] && ([ "$telegram_alert_type" = video+image ] || [ "$telegram_alert_type" = video ])) ||
([ "$send_matrix" = true ] && ([ "$matrix_alert_type" = video+image ] || [ "$matrix_alert_type" = video ])) ||
[ "$publish_mqtt_video" = true ]
then
record_video record_video
fi fi
# Next, start background tasks for all configured video notifications # Next, start background tasks for all configured video notifications
# Save the video # Save the video
if [ "$save_video" = true ] ; then if [ "$save_video" = true ] ; then
( (
@@ -252,11 +259,11 @@ if [ "$ftp_video" = true ]; then
fi fi
ftpput_cmd="$ftpput_cmd $ftp_host" ftpput_cmd="$ftpput_cmd $ftp_host"
# We only want one video stream at a time. Try to grab an # We only want one video stream at a time. Try to grab an exclusive
# exclusive flock on file descriptor 5. Bail out if another # flock on file descriptor 5. Bail out if another process already has
# process already has it. Touch the flock to update it's mod # it. Touch the flock to update it's mod time as a signal to the
# time as a signal to the background process to keep recording # background process to keep recording when motion is repeatedly
# when motion is repeatedly observed. # observed.
touch /run/ftp_motion_video_stream.flock touch /run/ftp_motion_video_stream.flock
exec 5<> /run/ftp_motion_video_stream.flock exec 5<> /run/ftp_motion_video_stream.flock
if /system/sdcard/bin/busybox flock -n -x 5; then if /system/sdcard/bin/busybox flock -n -x 5; then
@@ -265,10 +272,10 @@ if [ "$ftp_video" = true ]; then
# XXX Uses avconv to stitch multiple JPEGs into 1fps video. # XXX Uses avconv to stitch multiple JPEGs into 1fps video.
# I couldn't get it working another way. /dev/videoX inputs # I couldn't get it working another way. /dev/videoX inputs
# fail. Localhost rtsp takes very long (10+ seconds) to # fail. Localhost rtsp takes very long (10+ seconds) to start
# start streaming and gets flaky when when memory or cpu # streaming and gets flaky when when memory or cpu are pegged.
# are pegged. This is a clugy method, but works well even # This is a clugy method, but works well even at high res,
# at high res, fps, cpu, and memory load! # fps, cpu, and memory load!
( while [ "$(/system/sdcard/bin/busybox date "+%s")" -le "$(/system/sdcard/bin/busybox expr "$(/system/sdcard/bin/busybox stat -c "%X" /run/ftp_motion_video_stream.flock)" + "$video_duration")" ]; do ( while [ "$(/system/sdcard/bin/busybox date "+%s")" -le "$(/system/sdcard/bin/busybox expr "$(/system/sdcard/bin/busybox stat -c "%X" /run/ftp_motion_video_stream.flock)" + "$video_duration")" ]; do
/system/sdcard/bin/getimage /system/sdcard/bin/getimage
sleep 1 sleep 1
@@ -285,21 +292,17 @@ if [ "$ftp_video" = true ]; then
) & ) &
fi fi
#save Dropbox video # Save Dropbox video
if [ "$dropbox_video" = true ]; then if [ "$dropbox_video" = true ]; then
( (
debug_msg "Saving Dropbox snapshot to $dropbox_videos_dir/$filename.mp4"
debug_msg "Saving Dropbox snapshot to $dropbox_videos_dir/$filename.mp4" /system/sdcard/bin/curl -X POST "$dropbox_url" \
/system/sdcard/bin/curl -X POST https://content.dropboxapi.com/2/files/upload \ --header "Authorization: Bearer $dropbox_token" \
--header "Authorization: Bearer $dropbox_token" \ --header "Dropbox-API-Arg: {\"path\": \"$dropbox_videos_dir/$filename.mp4\"}" \
--header "Dropbox-API-Arg: {\"path\": \"$dropbox_videos_dir/$filename.mp4\"}" \ --header "Content-Type: application/octet-stream" \
--header "Content-Type: application/octet-stream" \ --data-binary @"$video_tempfile"
--data-binary @"$video_tempfile" ) &
) & fi
fi
# SMB snapshot and video # SMB snapshot and video
if [ "$smb_video" = true ]; then if [ "$smb_video" = true ]; then
@@ -342,14 +345,20 @@ if [ "$send_telegram" = true ]; then
( (
include /system/sdcard/config/telegram.conf include /system/sdcard/config/telegram.conf
if [ "$telegram_alert_type" = "video" -o "$telegram_alert_type" = "video+image" ] ; then if [ "$telegram_alert_type" = "video" -o "$telegram_alert_type" = "video+image" ] ; then
debug_msg "Send telegram video"
if [ "$video_use_rtsp" = true ]; then if [ "$video_use_rtsp" = true ]; then
#Convert file to mp4 and remove audio stream so video plays in telegram app if [ "$AUDIOFORMAT" = "PCMU" ] || [ "$AUDIOFORMAT" = "OFF" ] ; then
/system/sdcard/bin/avconv -i "$video_tempfile" -c:v copy -an "$video_tempfile"-telegram.mp4 # Convert file to mp4 and remove audio stream so video plays in telegram app
/system/sdcard/bin/telegram v "$video_tempfile"-telegram.mp4 debug_msg "Send telegram video"
rm "$video_tempfile"-telegram.mp4 /system/sdcard/bin/avconv -i "$video_tempfile" -c:v copy -an "$video_tempfile"-telegram.mp4
/system/sdcard/bin/telegram v "$video_tempfile"-telegram.mp4
rm "$video_tempfile"-telegram.mp4
else else
# avconv can't strip audio it doesn't understand
debug_msg "Send telegram video (only viable for external playback)"
/system/sdcard/bin/telegram v "$video_tempfile"
fi
else
/system/sdcard/bin/avconv -i "$video_tempfile" "$video_tempfile-lo.mp4" /system/sdcard/bin/avconv -i "$video_tempfile" "$video_tempfile-lo.mp4"
/system/sdcard/bin/telegram v "$video_tempfile-lo.mp4" /system/sdcard/bin/telegram v "$video_tempfile-lo.mp4"
rm "$video_tempfile-lo.mp4" rm "$video_tempfile-lo.mp4"
@@ -358,15 +367,33 @@ if [ "$send_telegram" = true ]; then
) & ) &
fi fi
# Send a matrix video
if [ "$send_matrix" = true ]; then
(
include /system/sdcard/config/matrix.conf
if [ "$matrix_alert_type" = "video" -o "$matrix_alert_type" = "video+image" ] ; then
debug_msg "Send matrix video"
if [ "$video_use_rtsp" = true ]; then
/system/sdcard/bin/matrix v "$filename" "$video_tempfile"
else
/system/sdcard/bin/avconv -i "$video_tempfile" "$video_tempfile-lo.mp4"
/system/sdcard/bin/matrix v "$filename" "$video_tempfile-lo.mp4"
rm -f "$video_tempfile-lo.mp4"
fi
fi
) &
fi
# Run any user scripts. # Run any user scripts.
for i in /system/sdcard/config/userscripts/motiondetection/*; do for i in /system/sdcard/config/userscripts/motiondetection/*; do
if [ -x "$i" ]; then if [ -x "$i" ]; then
debug_msg "Running: $i on $snapshot_tempfile" debug_msg "Running: $i on $snapshot_tempfile"
$i on "$snapshot_tempfile" "$video_tempfile" & $i on "$snapshot_tempfile" "$video_tempfile" &
fi fi
done done
# Wait for all background jobs to finish before existing and deleting tempfile # Wait for all background jobs to finish before exiting and deleting tempfile
debug_msg "Waiting for background jobs to end:" debug_msg "Waiting for background jobs to end:"
for jobpid in $(jobs -p); do for jobpid in $(jobs -p); do
wait "$jobpid" wait "$jobpid"

View File

@@ -11,6 +11,10 @@ JQ="/system/sdcard/bin/jq"
[ -z $apiToken ] && echo "api token not configured yet" && exit 1 [ -z $apiToken ] && echo "api token not configured yet" && exit 1
[ -z $userChatId ] && echo "chat id not configured yet" && exit 1 [ -z $userChatId ] && echo "chat id not configured yet" && exit 1
status() {
$TELEGRAM m "Motion detection `motion_detection status`\nNight mode `night_mode status`\nAlert type `get_config /system/sdcard/config/motion.conf telegram_alert_type`"
}
sendShot() { sendShot() {
/system/sdcard/bin/getimage > "/tmp/telegram_image.jpg" &&\ /system/sdcard/bin/getimage > "/tmp/telegram_image.jpg" &&\
$TELEGRAM p "/tmp/telegram_image.jpg" $TELEGRAM p "/tmp/telegram_image.jpg"
@@ -52,10 +56,16 @@ videoAlerts() {
$TELEGRAM m "Video alerts on motion detection enabled" $TELEGRAM m "Video alerts on motion detection enabled"
} }
imageThenVideoAlerts() {
rewrite_config /system/sdcard/config/motion.conf telegram_alert_type "video+image"
$TELEGRAM m "Image then video alerts on motion detection enabled"
}
respond() { respond() {
cmd=$1 cmd=$1
[ $chatId -lt 0 ] && cmd=${1%%@*} [ $chatId -lt 0 ] && cmd=${1%%@*}
case $cmd in case $cmd in
/status) status;;
/mem) sendMem;; /mem) sendMem;;
/shot) sendShot;; /shot) sendShot;;
/on) detectionOn;; /on) detectionOn;;
@@ -65,7 +75,8 @@ respond() {
/textalerts) textAlerts;; /textalerts) textAlerts;;
/imagealerts) imageAlerts;; /imagealerts) imageAlerts;;
/videoalerts) videoAlerts;; /videoalerts) videoAlerts;;
/help | /start) $TELEGRAM m "######### Bot commands #########\n# /mem - show memory information\n# /shot - take a snapshot\n# /on - motion detection on\n# /off - motion detection off\n# /nighton - night mode on\n# /nightoff - night mode off\n# /textalerts - Text alerts on motion detection\n# /imagealerts - Image alerts on motion detection\n# /videoalerts - Video alerts on motion detection";; /dualalerts) imageThenVideoAlerts;;
/help | /start) $TELEGRAM m "######### Bot commands #########\n# /mem - show memory information\n# /status - show current camera status\n# /shot - take a snapshot\n# /on - motion detection on\n# /off - motion detection off\n# /nighton - night mode on\n# /nightoff - night mode off\n# /textalerts - Text alerts on motion detection\n# /imagealerts - Image alerts on motion detection\n# /videoalerts - Video alerts on motion detection\n# /dualalerts - Image snapshot then video alerts on motion detection";;
/*) $TELEGRAM m "I can't respond to '$cmd' command" /*) $TELEGRAM m "I can't respond to '$cmd' command"
esac esac
} }
@@ -109,7 +120,8 @@ main() {
if [ "$chatId" != "$userChatId" ]; then if [ "$chatId" != "$userChatId" ]; then
username=$(echo "$json" | $JQ -r ".result[0].$messageAttr.from.username // \"\"") username=$(echo "$json" | $JQ -r ".result[0].$messageAttr.from.username // \"\"")
firstName=$(echo "$json" | $JQ -r ".result[0].$messageAttr.from.first_name // \"\"") firstName=$(echo "$json" | $JQ -r ".result[0].$messageAttr.from.first_name // \"\"")
$TELEGRAM m "Received message from unauthorized chat id: $chatId\nUser: $username($firstName)\nMessage: $cmd" # Uncomment to get notified of attempted chat spam
# $TELEGRAM m "Received message from unauthorized chat id: $chatId\nUser: $username($firstName)\nMessage: $cmd"
else else
respond $cmd respond $cmd
fi; fi;

View File

@@ -0,0 +1,14 @@
#!/bin/sh
echo "Content-type: text/html"
echo "Pragma: no-cache"
echo "Cache-Control: max-age=0, no-store, no-cache"
echo ""
if [ "${REQUEST_METHOD}" = "POST" ]
then
in_raw=`dd bs=1 count=${CONTENT_LENGTH} 1>/tmp/playback.wav`
sed -i -e '1,/Content-Type:/d' /tmp/playback.wav
echo " CONTENT LENGTH ${CONTENT_LENGTH}"
/system/sdcard/bin/audioplay /tmp/playback.wav 60
fi

View File

@@ -41,7 +41,7 @@ if [ -n "$F_cmd" ]; then
do do
if [[ -f $file ]]; then if [[ -f $file ]]; then
file_size=$(ls -lh $file | awk '{print $5}') file_size=$(ls -lh $file | awk '{print $5}')
file_url=$(ls -lh $file | awk '{print $9}' | sed 's/\/system\/sdcard\/DCIM/\/viewer/') file_url=$(ls -lh $file | awk '{print $9}' | sed 's/\/system\/sdcard\/DCIM/viewer/')
file_date=$(ls -lh $file | awk '{print $6 "-" $7 "-" $8}') file_date=$(ls -lh $file | awk '{print $6 "-" $7 "-" $8}')
file_name=$(ls -lh $file | awk '{print $9}' | awk -F / '{print $(NF)}') file_name=$(ls -lh $file | awk '{print $9}' | awk -F / '{print $(NF)}')
echo "${file_name}#:#${file_size}#:#${file_date}#:#${file_url}" echo "${file_name}#:#${file_size}#:#${file_date}#:#${file_url}"
@@ -51,12 +51,12 @@ if [ -n "$F_cmd" ]; then
;; ;;
del_config) del_config)
F_file=$(echo ${F_file} | sed -e 's/%2F/\//g' | sed -e 's/viewer/system\/sdcard\/DCIM/') F_file=$(echo ${F_file} | sed -e 's/%2F/\//g' | sed -e 's/viewer/\/system\/sdcard\/DCIM/')
echo "Remove ${F_file}" echo "Remove ${F_file}"
rm $F_file rm $F_file
;; ;;
restore_config) restore_config)
F_file=$(echo ${F_file} | sed -e 's/%2F/\//g' | sed -e 's/viewer/system\/sdcard\/DCIM/') F_file=$(echo ${F_file} | sed -e 's/%2F/\//g' | sed -e 's/viewer/\/system\/sdcard\/DCIM/')
tar -xf $F_file -C /system/sdcard/config/ tar -xf $F_file -C /system/sdcard/config/
echo "Restore done" echo "Restore done"
/sbin/reboot /sbin/reboot

View File

@@ -40,6 +40,7 @@ if [ -n "$F_cmd" ]; then
echo "dropboxVideosDir#:#${dropbox_videos_dir}" echo "dropboxVideosDir#:#${dropbox_videos_dir}"
echo "dropboxSnapshot#:#${dropbox_snapshot}" echo "dropboxSnapshot#:#${dropbox_snapshot}"
echo "dropboxVideo#:#${dropbox_video}" echo "dropboxVideo#:#${dropbox_video}"
echo "dropboxUrl#:#${dropbox_url}"
echo "dropboxToken#:#${dropbox_token}" echo "dropboxToken#:#${dropbox_token}"
echo "smbSnapshot#:#${smb_snapshot}" echo "smbSnapshot#:#${smb_snapshot}"
echo "smbVideo#:#${smb_video}" echo "smbVideo#:#${smb_video}"
@@ -56,6 +57,7 @@ if [ -n "$F_cmd" ]; then
echo "sendTelegram#:#${send_telegram}" echo "sendTelegram#:#${send_telegram}"
echo "telegramAlertType#:#${telegram_alert_type}" echo "telegramAlertType#:#${telegram_alert_type}"
echo "sendMatrix#:#${send_matrix}" echo "sendMatrix#:#${send_matrix}"
echo "matrixAlertType#:#${matrix_alert_type}"
echo "nightModeEventDelay#:#${night_mode_event_delay}" echo "nightModeEventDelay#:#${night_mode_event_delay}"
;; ;;
@@ -197,6 +199,11 @@ if [ -n "$F_cmd" ]; then
rewrite_config /system/sdcard/config/motion.conf dropbox_video $F_dropboxVideo rewrite_config /system/sdcard/config/motion.conf dropbox_video $F_dropboxVideo
echo "Save video to dropbox set to $F_dropboxVideo<br/>" echo "Save video to dropbox set to $F_dropboxVideo<br/>"
fi fi
if [ -n "${F_dropboxUrl+x}" ]; then
F_dropboxUrl=$(printf '%b' "${F_dropboxUrl//%/\\x}" | sed 's/\//\\\//g')
rewrite_config /system/sdcard/config/motion.conf dropbox_url "\"$F_dropboxUrl\""
echo "dropbox url set<br/>"
fi
if [ -n "${F_dropboxToken+x}" ]; then if [ -n "${F_dropboxToken+x}" ]; then
F_dropboxToken=$(printf '%b' "${F_dropboxToken//%/\\x}" | sed 's/\//\\\//g') F_dropboxToken=$(printf '%b' "${F_dropboxToken//%/\\x}" | sed 's/\//\\\//g')
rewrite_config /system/sdcard/config/motion.conf dropbox_token "\"$F_dropboxToken\"" rewrite_config /system/sdcard/config/motion.conf dropbox_token "\"$F_dropboxToken\""
@@ -252,6 +259,11 @@ if [ -n "$F_cmd" ]; then
rewrite_config /system/sdcard/config/motion.conf send_matrix $F_sendMatrix rewrite_config /system/sdcard/config/motion.conf send_matrix $F_sendMatrix
echo "Send Matrix on motion set to $F_sendMatrix<br/>" echo "Send Matrix on motion set to $F_sendMatrix<br/>"
fi fi
if [ -n "${F_matrixAlertType+x}" ]; then
F_matrixAlertType=$(printf '%b' "${F_matrixAlertType//%/\\x}")
rewrite_config /system/sdcard/config/motion.conf matrix_alert_type $F_matrixAlertType
echo "Matrix alert type set to $F_matrixAlertType<br/>"
fi
if [ -n "${F_nightModeEventDelay+x}" ]; then if [ -n "${F_nightModeEventDelay+x}" ]; then
F_nightModeEventDelay=$(printf '%b' "${F_nightModeEventDelay//%/\\x}") F_nightModeEventDelay=$(printf '%b' "${F_nightModeEventDelay//%/\\x}")
rewrite_config /system/sdcard/config/motion.conf night_mode_event_delay $F_nightModeEventDelay rewrite_config /system/sdcard/config/motion.conf night_mode_event_delay $F_nightModeEventDelay

View File

@@ -31,7 +31,7 @@ if [ -n "$F_cmd" ]; then
do do
if [[ -f $file ]]; then if [[ -f $file ]]; then
file_size=$(ls -lh $file | awk '{print $5}') file_size=$(ls -lh $file | awk '{print $5}')
file_url=$(ls -lh $file | awk '{print $9}' | sed 's/\/system\/sdcard\/DCIM/\/viewer/') file_url=$(ls -lh $file | awk '{print $9}' | sed 's/\/system\/sdcard\/DCIM/viewer/')
file_date=$(ls -lh $file | awk '{print $6 "-" $7 "-" $8}') file_date=$(ls -lh $file | awk '{print $6 "-" $7 "-" $8}')
file_name=$(ls -lh $file | awk '{print $9}' | awk -F / '{print $(NF)}') file_name=$(ls -lh $file | awk '{print $9}' | awk -F / '{print $(NF)}')
echo "${file_name}#:#${file_size}#:#${file_date}#:#${file_url}" echo "${file_name}#:#${file_size}#:#${file_date}#:#${file_url}"
@@ -45,7 +45,7 @@ if [ -n "$F_cmd" ]; then
echo "sdcardUsedPercent#:#$(df -h /system/sdcard | awk 'NR==2{print$5}')" echo "sdcardUsedPercent#:#$(df -h /system/sdcard | awk 'NR==2{print$5}')"
;; ;;
del_file) del_file)
F_file=$(echo ${F_file} | sed -e 's/%2F/\//g' | sed -e 's/viewer/system\/sdcard\/DCIM/') F_file=$(echo ${F_file} | sed -e 's/%2F/\//g' | sed -e 's/viewer/\/system\/sdcard\/DCIM/')
echo "Remove ${F_file}" echo "Remove ${F_file}"
rm $F_file rm $F_file
;; ;;
@@ -54,4 +54,4 @@ if [ -n "$F_cmd" ]; then
;; ;;
esac esac
fi fi

View File

@@ -17,12 +17,12 @@
<script type="text/javascript" src="lib/datatable/datatables.min.js"></script> <script type="text/javascript" src="lib/datatable/datatables.min.js"></script>
<!-- smoothie--> <!-- smoothie-->
<script type="text/javascript" src="lib/smoothie/smoothie.js"></script> <script type="text/javascript" src="lib/smoothie/smoothie.js"></script>
<script src="/lib/d3/d3.min.js"></script> <script src="lib/d3/d3.min.js"></script>
<script src="/lib/event-drops/index.js"></script> <script src="lib/event-drops/index.js"></script>
<!-- Dafang hack CSS--> <!-- Dafang hack CSS-->
<link rel="stylesheet" href="css/w3-toggle.css" type="text/css" /> <link rel="stylesheet" href="css/w3-toggle.css" type="text/css" />
<link rel="stylesheet" href="css/w3-accordion.css" type="text/css" /> <link rel="stylesheet" href="css/w3-accordion.css" type="text/css" />
<link href="/lib/event-drops/style.css" rel="stylesheet" /> <link href="lib/event-drops/style.css" rel="stylesheet" />
<script type="text/javascript"> <script type="text/javascript">
//Check if theme configured //Check if theme configured
var css = localStorage.getItem('theme') var css = localStorage.getItem('theme')

View File

@@ -0,0 +1,85 @@
var gumStream; //stream from getUserMedia()
var rec; //Recorder.js object
var input; //MediaStreamAudioSourceNode we'll be recording
// shim for AudioContext when it's not avb.
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext //audio context to help us record
function startRecording() {
console.log("Recording started");
/*
Simple constraints object, for more advanced audio features see
https://addpipe.com/blog/audio-constraints-getusermedia/
*/
var constraints = {
audio: true,
video: false
}
/*
We're using the standard promise based getUserMedia()
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
*/
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
console.log("getUserMedia() success, stream created, initializing Recorder.js ...");
/*
create an audio context after getUserMedia is called
sampleRate might change after getUserMedia is called, like it does on macOS when recording through AirPods
the sampleRate defaults to the one set in your OS for your playback device
*/
audioContext = new AudioContext({
sampleRate: 8000
});
/* assign to gumStream for later use */
gumStream = stream;
/* use the stream */
input = audioContext.createMediaStreamSource(stream);
/*
Create the Recorder object and configure to record mono sound (1 channel)
Recording 2 channels will double the file size
*/
rec = new Recorder(input, {
numChannels: 1
})
//start the recording process
rec.record()
});
}
function stopRecording() {
//tell the recorder to stop the recording
// The Dafang fails to play the last two seconds because some bug on audioplay...
setTimeout(() => {
rec.stop();
//stop microphone access
gumStream.getAudioTracks()[0].stop();
rec.exportWAV(sendAudio);
function sendAudio(blob) {
var xhr = new XMLHttpRequest();
xhr.onload = function (e) {
if (this.readyState === 4) {
console.log("Server returned: ", e.target.responseText);
}
};
var fd = new FormData();
fd.append("audio_data", blob, 'recording.wav');
xhr.open("POST", "cgi-bin/audio_upload.cgi", true);
xhr.send(fd);
}
console.log("Audio sent");
}, 2000);
}

View File

@@ -0,0 +1,357 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Recorder = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
module.exports = require("./recorder").Recorder;
},{"./recorder":2}],2:[function(require,module,exports){
'use strict';
var _createClass = (function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
}
}return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
};
})();
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Recorder = undefined;
var _inlineWorker = require('inline-worker');
var _inlineWorker2 = _interopRequireDefault(_inlineWorker);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Recorder = exports.Recorder = (function () {
function Recorder(source, cfg) {
var _this = this;
_classCallCheck(this, Recorder);
this.config = {
bufferLen: 4096,
numChannels: 2,
mimeType: 'audio/wav'
};
this.recording = false;
this.callbacks = {
getBuffer: [],
exportWAV: []
};
Object.assign(this.config, cfg);
this.context = source.context;
this.node = (this.context.createScriptProcessor || this.context.createJavaScriptNode).call(this.context, this.config.bufferLen, this.config.numChannels, this.config.numChannels);
this.node.onaudioprocess = function (e) {
if (!_this.recording) return;
var buffer = [];
for (var channel = 0; channel < _this.config.numChannels; channel++) {
buffer.push(e.inputBuffer.getChannelData(channel));
}
_this.worker.postMessage({
command: 'record',
buffer: buffer
});
};
source.connect(this.node);
this.node.connect(this.context.destination); //this should not be necessary
var self = {};
this.worker = new _inlineWorker2.default(function () {
var recLength = 0,
recBuffers = [],
sampleRate = undefined,
numChannels = undefined;
self.onmessage = function (e) {
switch (e.data.command) {
case 'init':
init(e.data.config);
break;
case 'record':
record(e.data.buffer);
break;
case 'exportWAV':
exportWAV(e.data.type);
break;
case 'getBuffer':
getBuffer();
break;
case 'clear':
clear();
break;
}
};
function init(config) {
sampleRate = config.sampleRate;
numChannels = config.numChannels;
initBuffers();
}
function record(inputBuffer) {
for (var channel = 0; channel < numChannels; channel++) {
recBuffers[channel].push(inputBuffer[channel]);
}
recLength += inputBuffer[0].length;
}
function exportWAV(type) {
var buffers = [];
for (var channel = 0; channel < numChannels; channel++) {
buffers.push(mergeBuffers(recBuffers[channel], recLength));
}
var interleaved = undefined;
if (numChannels === 2) {
interleaved = interleave(buffers[0], buffers[1]);
} else {
interleaved = buffers[0];
}
var dataview = encodeWAV(interleaved);
var audioBlob = new Blob([dataview], { type: type });
self.postMessage({ command: 'exportWAV', data: audioBlob });
}
function getBuffer() {
var buffers = [];
for (var channel = 0; channel < numChannels; channel++) {
buffers.push(mergeBuffers(recBuffers[channel], recLength));
}
self.postMessage({ command: 'getBuffer', data: buffers });
}
function clear() {
recLength = 0;
recBuffers = [];
initBuffers();
}
function initBuffers() {
for (var channel = 0; channel < numChannels; channel++) {
recBuffers[channel] = [];
}
}
function mergeBuffers(recBuffers, recLength) {
var result = new Float32Array(recLength);
var offset = 0;
for (var i = 0; i < recBuffers.length; i++) {
result.set(recBuffers[i], offset);
offset += recBuffers[i].length;
}
return result;
}
function interleave(inputL, inputR) {
var length = inputL.length + inputR.length;
var result = new Float32Array(length);
var index = 0,
inputIndex = 0;
while (index < length) {
result[index++] = inputL[inputIndex];
result[index++] = inputR[inputIndex];
inputIndex++;
}
return result;
}
function floatTo16BitPCM(output, offset, input) {
for (var i = 0; i < input.length; i++, offset += 2) {
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
function writeString(view, offset, string) {
for (var i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
function encodeWAV(samples) {
var buffer = new ArrayBuffer(44 + samples.length * 2);
var view = new DataView(buffer);
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* RIFF chunk length */
view.setUint32(4, 36 + samples.length * 2, true);
/* RIFF type */
writeString(view, 8, 'WAVE');
/* format chunk identifier */
writeString(view, 12, 'fmt ');
/* format chunk length */
view.setUint32(16, 16, true);
/* sample format (raw) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, numChannels, true);
/* sample rate */
view.setUint32(24, sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, sampleRate * 4, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, numChannels * 2, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, samples.length * 2, true);
floatTo16BitPCM(view, 44, samples);
return view;
}
}, self);
this.worker.postMessage({
command: 'init',
config: {
sampleRate: this.context.sampleRate,
numChannels: this.config.numChannels
}
});
this.worker.onmessage = function (e) {
var cb = _this.callbacks[e.data.command].pop();
if (typeof cb == 'function') {
cb(e.data.data);
}
};
}
_createClass(Recorder, [{
key: 'record',
value: function record() {
this.recording = true;
}
}, {
key: 'stop',
value: function stop() {
this.recording = false;
}
}, {
key: 'clear',
value: function clear() {
this.worker.postMessage({ command: 'clear' });
}
}, {
key: 'getBuffer',
value: function getBuffer(cb) {
cb = cb || this.config.callback;
if (!cb) throw new Error('Callback not set');
this.callbacks.getBuffer.push(cb);
this.worker.postMessage({ command: 'getBuffer' });
}
}, {
key: 'exportWAV',
value: function exportWAV(cb, mimeType) {
mimeType = mimeType || this.config.mimeType;
cb = cb || this.config.callback;
if (!cb) throw new Error('Callback not set');
this.callbacks.exportWAV.push(cb);
this.worker.postMessage({
command: 'exportWAV',
type: mimeType
});
}
}], [{
key: 'forceDownload',
value: function forceDownload(blob, filename) {
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var link = window.document.createElement('a');
link.href = url;
link.download = filename || 'output.wav';
var click = document.createEvent("Event");
click.initEvent("click", true, true);
link.dispatchEvent(click);
}
}]);
return Recorder;
})();
exports.default = Recorder;
},{"inline-worker":3}],3:[function(require,module,exports){
"use strict";
module.exports = require("./inline-worker");
},{"./inline-worker":4}],4:[function(require,module,exports){
(function (global){
"use strict";
var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
var WORKER_ENABLED = !!(global === global.window && global.URL && global.Blob && global.Worker);
var InlineWorker = (function () {
function InlineWorker(func, self) {
var _this = this;
_classCallCheck(this, InlineWorker);
if (WORKER_ENABLED) {
var functionBody = func.toString().trim().match(/^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/)[1];
var url = global.URL.createObjectURL(new global.Blob([functionBody], { type: "text/javascript" }));
return new global.Worker(url);
}
this.self = self;
this.self.postMessage = function (data) {
setTimeout(function () {
_this.onmessage({ data: data });
}, 0);
};
setTimeout(function () {
func.call(self);
}, 0);
}
_createClass(InlineWorker, {
postMessage: {
value: function postMessage(data) {
var _this = this;
setTimeout(function () {
_this.self.onmessage({ data: data });
}, 0);
}
}
});
return InlineWorker;
})();
module.exports = InlineWorker;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[1])(1)
});

View File

@@ -51,7 +51,7 @@ function accordion() {
function checkSDCard() { function checkSDCard() {
$.get("/cgi-bin/ui_sdcard.cgi", {cmd: "check_sdcard"}, function (result) { $.get("cgi-bin/ui_sdcard.cgi", {cmd: "check_sdcard"}, function (result) {
if ( result == "nok") { if ( result == "nok") {
$('#notifAlarm').attr('style','color:red'); $('#notifAlarm').attr('style','color:red');
$('#notifContent').html("<p></p>Your sdcard is mounted read-only. Settings can't be saved. \ $('#notifContent').html("<p></p>Your sdcard is mounted read-only. Settings can't be saved. \

View File

@@ -4,24 +4,24 @@ var stateSideBar = false;
//Function for flip image //Function for flip image
function flip() { function flip() {
$.get("/cgi-bin/ui_live.cgi", {cmd: "flip"}); $.get("cgi-bin/ui_live.cgi", {cmd: "flip"});
} }
//Functions for live stream with images //Functions for live stream with images
function refreshLiveImage() { function refreshLiveImage() {
var ts = new Date().getTime(); var ts = new Date().getTime();
$("#liveview").attr("src", "/cgi-bin/currentpic.cgi?" + ts); $("#liveview").attr("src", "cgi-bin/currentpic.cgi?" + ts);
} }
//Function to download a screenshot //Function to download a screenshot
function downloadScreenshot() { function downloadScreenshot() {
var ts = new Date().getTime(); var ts = new Date().getTime();
window.location.href = "/cgi-bin/downloadpic.cgi?" + ts; window.location.href = "cgi-bin/downloadpic.cgi?" + ts;
} }
//Function to refresh side bar buttons //Function to refresh side bar buttons
function refreshSideBar() { function refreshSideBar() {
$.get("/cgi-bin/ui_live.cgi", {cmd: "status_all"}, function (result) { $.get("cgi-bin/ui_live.cgi", {cmd: "status_all"}, function (result) {
var switches = result.split("\n"); var switches = result.split("\n");
for (var i = 0; i < switches.length-1; i++) { for (var i = 0; i < switches.length-1; i++) {
var switch_info = switches[i].split(":"); var switch_info = switches[i].split(":");
@@ -70,6 +70,21 @@ function PTZControl(view) {
} }
} }
function pushToTalk(action) {
if (action == "on") {
$("#btn-ptt span").attr("style","color:red");
$("#btn-ptt span").removeAttr("onpointerdown");
$("#btn-ptt").attr("onpointerup","pushToTalk('off')");
startRecording();
}
else {
stopRecording();
$("#btn-ptt span").removeAttr("style");
$("#btn-ptt span").removeAttr("onpointerup");
$("#btn-ptt").attr("onpointerdown","pushToTalk('on')");
}
}
function record(action) { function record(action) {
$.get("cgi-bin/ui_live.cgi",{cmd: "recording", action: action},function (result) { $.get("cgi-bin/ui_live.cgi",{cmd: "recording", action: action},function (result) {
if (action == "on" || result == "ON\n") { if (action == "on" || result == "ON\n") {

View File

@@ -1,11 +1,12 @@
//Function to delete a file //Function to delete a file
function deleteFile(fileName,dir,confirm) { function deleteFile(fileName,dir,askuser) {
if (confirm) if (askuser) {
var del = confirm("Confirm delete file: "+fileName); var del = confirm("Confirm delete file: "+fileName);
if ( del ) { if ( del ) {
$.get("cgi-bin/ui_sdcard.cgi", {cmd: "del_file",file: fileName}); $.get("cgi-bin/ui_sdcard.cgi", {cmd: "del_file",file: fileName});
getFiles(dir); getFiles(dir);
} }
}
else { else {
$.get("cgi-bin/ui_sdcard.cgi", {cmd: "del_file",file: fileName}); $.get("cgi-bin/ui_sdcard.cgi", {cmd: "del_file",file: fileName});
getFiles(dir); getFiles(dir);
@@ -48,7 +49,7 @@ function getFiles(dir) {
<td>"+config_info[2]+"</td> \ <td>"+config_info[2]+"</td> \
<td> \ <td> \
<a href=\""+config_info[3]+"\" download><i class='fas fa-download' title='Download file'></i></a> \ <a href=\""+config_info[3]+"\" download><i class='fas fa-download' title='Download file'></i></a> \
<span onclick=\"deleteFile('"+config_info[3]+"','"+dir+",true')\"><i class='fas fa-trash' title='Delete file'></i></span>\ <span onclick=\"deleteFile('"+config_info[3]+"','"+dir+"',true)\"><i class='fas fa-trash' title='Delete file'></i></span>\
"+html_photo+"\ "+html_photo+"\
</td></tr>"); </td></tr>");
} }

View File

@@ -15,7 +15,7 @@ var iridix = new TimeSeries();
var gain = new TimeSeries(); var gain = new TimeSeries();
function readIspInfo(){ function readIspInfo(){
$.get("/cgi-bin/ui_softnight.cgi", function(data, status) $.get("cgi-bin/ui_softnight.cgi", function(data, status)
{ {
parseIspInfo(data); parseIspInfo(data);
}); });
@@ -78,7 +78,7 @@ function startGraph(){
} }
$(document).ready(function () { $(document).ready(function () {
$.get("/cgi-bin/action.cgi?cmd=get_sw_night_config", function(data, status) $.get("cgi-bin/action.cgi?cmd=get_sw_night_config", function(data, status)
{ {
var list = data.split('-'); var list = data.split('-');
var valid=false; var valid=false;
@@ -365,4 +365,4 @@ $('#swNightForm').submit(function (event) {
showResult(res); showResult(res);
}); });
event.preventDefault(); event.preventDefault();
}); });

View File

@@ -2,7 +2,7 @@
<div class="w3-panel w3-card"> <div class="w3-panel w3-card">
<p id="hostname"> Hostname</p> <p id="hostname"> Hostname</p>
<div class="w3-display-container" onmouseover="camControl('show')" onmouseleave="camControl('hide');PTZControl('hide')"> <div class="w3-display-container" onmouseover="camControl('show')" onmouseleave="camControl('hide');PTZControl('hide')">
<img id="liveview" src="/cgi-bin/currentpic.cgi" onerror="this.src='img/unable_load.png';" alt="Camera" style="width:100%" onmouseover="camControl('show')" onmouseout="camControl('hide')" > <img id="liveview" src="cgi-bin/currentpic.cgi" onerror="this.src='img/unable_load.png';" alt="Camera" style="width:100%" onmouseover="camControl('show')" onmouseout="camControl('hide')" >
<div id="btn-ptz-left" class="w3-display-left w3-container w3-hide"> <div id="btn-ptz-left" class="w3-display-left w3-container w3-hide">
<button class="w3-button w3-circle w3-black" onclick="moveCamera('left')"><i class="fa fa-angle-left"></i></button> <button class="w3-button w3-circle w3-black" onclick="moveCamera('left')"><i class="fa fa-angle-left"></i></button>
</div> </div>
@@ -23,6 +23,7 @@
<button class="w3-button w3-circle w3-black" onclick="flip()">Flip</button> <button class="w3-button w3-circle w3-black" onclick="flip()">Flip</button>
</div> </div>
<div id="cam-control" class="w3-display-bottomright w3-container w3-hide"> <div id="cam-control" class="w3-display-bottomright w3-container w3-hide">
<button id="btn-ptt" class="w3-button w3-circle w3-black" onpointerdown="pushToTalk('on')" title="Push To Talk"><span><i class="fas fa-microphone"></i></span></button>
<button id="btn-record" class="w3-button w3-circle w3-black" onclick="record('on')" title="Record On/Off"><span><i class="fas fa-video"></i></span></button> <button id="btn-record" class="w3-button w3-circle w3-black" onclick="record('on')" title="Record On/Off"><span><i class="fas fa-video"></i></span></button>
<button id="btn-screenshot" class="w3-button w3-circle w3-black" onclick="downloadScreenshot()" title="Download Screenshot"><span><i class="fas fa-image"></i></span></button> <button id="btn-screenshot" class="w3-button w3-circle w3-black" onclick="downloadScreenshot()" title="Download Screenshot"><span><i class="fas fa-image"></i></span></button>
<button id="btn-ptz" class="w3-button w3-circle w3-black" onclick="PTZControl('show')" title="PTZ Control"> <i class="fa fa-arrows-alt"></i></button> <button id="btn-ptz" class="w3-button w3-circle w3-black" onclick="PTZControl('show')" title="PTZ Control"> <i class="fa fa-arrows-alt"></i></button>
@@ -33,7 +34,6 @@
<p></p> <p></p>
</div> </div>
<script type="text/javascript" src="js/audio_recorder.js"></script>
<script type="text/javascript" src="js/audio_capture.js"></script>
<script type="text/javascript" src="js/live.js"></script>
<script type="text/javascript" src="js/live.js"></script>

View File

@@ -215,18 +215,14 @@
<button class="accordion" type='button'>Dropbox storage</button> <button class="accordion" type='button'>Dropbox storage</button>
<div class="panel"> <div class="panel">
<p></p> <p></p>
<div class="w3-row-padding"> <div class="w3-container">
<div class="w3-half"> <label>Dropbox URL</label>
<label>Dropbox Long Lived Token</label> <input class="w3-input" id="dropboxUrl" type="text">
<input class="w3-input" id="dropboxToken">
</div>
</div> </div>
<p></p> <p></p>
<div class="w3-row-padding"> <div class="w3-container">
<div class="w3-half"> <label>Dropbox Long Lived Token</label>
</div> <input class="w3-input" id="dropboxToken" type="text">
<div class="w3-half">
</div>
</div> </div>
<p></p> <p></p>
<div class="w3-row-padding"> <div class="w3-row-padding">
@@ -321,6 +317,15 @@
<option value="true">Activate</option> <option value="true">Activate</option>
</select> </select>
</div> </div>
<div class="w3-container">
<label>Matrix alert type</label>
<select id="matrixAlertType" class="w3-select" name="option">
<option value="text">Text</option>
<option value="image">Image</option>
<option value="video">Video</option>
<option value="video+image">Video and Image</option>
</select>
</div>
<div class="w3-container"> <div class="w3-container">
<label>Night mode delay (ignore motion events for 'delay' seconds after switching to night mode)</label> <label>Night mode delay (ignore motion events for 'delay' seconds after switching to night mode)</label>
<input class="w3-input" id="nightModeEventDelay" type="number" size="6"> <input class="w3-input" id="nightModeEventDelay" type="number" size="6">

View File

@@ -0,0 +1,142 @@
<?php
/* A simple Dropbox-like API for your own server
Copyright (C) 2021 Chris Osgood
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
##### CONFIG ###############################################################
# Change this to some random long password.
# This key should match the key under Settings->Motion Settings->Storage->Dropbox storage->Dropbox Long Lived Token.
$APIKEY = "aw4QrDrz5Z94oyaYLx9ojh8ZDARt7tA8hRqosNG305shGD6NJtAcjOC3WS3odsBMdkPFvl0hJSZyIkCJ";
# This is the directory where all images and video will be stored. Missing subdirectories will be created here
# based on the values of "Dropbox snapshots remote directory" and "Dropbox videos remote directory".
# Make sure the web user has access permissions for this directory.
$OUTDIR = "/media/securitycam";
# Allowed file extensions
$ALLOWEDFILETYPES = array('jpg', 'mp4', 'avi');
# Maximum upload file size (50MB shown)
$MAXFILESIZE = 50 * 1024 * 1024;
############################################################################
if (!array_key_exists('HTTP_AUTHORIZATION', $_SERVER) || !array_key_exists('HTTP_DROPBOX_API_ARG', $_SERVER))
{
header("HTTP/1.1 500 Internal Server Error");
error_log("ERROR: Missing authorization or API argument");
exit();
}
if ($_SERVER['HTTP_AUTHORIZATION'] != "Bearer $APIKEY")
{
header("HTTP/1.1 403 Forbidden");
exit();
}
$path = json_decode($_SERVER['HTTP_DROPBOX_API_ARG'], true);
if ($path == null || !array_key_exists('path', $path))
{
header("HTTP/1.1 500 Internal Server Error");
error_log("ERROR: Missing or bad file path `".$_SERVER['HTTP_DROPBOX_API_ARG']."'");
exit();
}
$path = $path['path'];
#----------- PATH SANITIZING -----------------------
# Only allow basic characters and digits
$path = str_replace("\\", "/", $path);
$path = preg_replace("([^a-zA-Z\d \-_./])", "_", $path);
# Clean beginning of path
$path = preg_replace("(^[/.]+)", "", $path);
# No path backwards; NOTE: this is a simplistic approach that will
# disallow any paths with multiple repeating periods.
$path = preg_replace("(\.+)", ".", $path);
# Clean repeated path seperators
$path = preg_replace("(\/+)", "/", $path);
# Remove all "current directory" paths
do {
$oldpath = $path;
$path = str_replace("/./", "/", $path);
} while ($path != $oldpath);
#---------------------------------------------------
$dirname = dirname($path);
$filename = basename($path);
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if (!$ext || strlen($filename) < 1)
{
header("HTTP/1.1 500 Internal Server Error");
error_log("ERROR: Bad file path");
exit();
}
if (!in_array(strtolower($ext), $ALLOWEDFILETYPES))
{
header("HTTP/1.1 403 Forbidden");
error_log("ERROR: File extension not allowed");
exit();
}
$dirname = "$OUTDIR/$dirname";
$filename = "$dirname/$filename";
if (!is_dir($dirname))
{
if (!mkdir($dirname, 0777, true))
{
header("HTTP/1.1 500 Internal Server Error");
error_log("ERROR: Failed to create output directory `$dirname'");
exit();
}
}
if (file_exists($filename))
{
header("HTTP/1.1 500 Internal Server Error");
error_log("ERROR: Output file already exists `$filename'");
exit();
}
if (!($fp = fopen($filename, "wb")))
{
header("HTTP/1.1 500 Internal Server Error");
error_log("ERROR: Failed to create output file `$filename'");
exit();
}
$data = file_get_contents('php://input', false, null, 0, $MAXFILESIZE);
$numbytes = fwrite($fp, $data);
fclose($fp);
if (!$numbytes)
{
header("HTTP/1.1 500 Internal Server Error");
error_log("ERROR: Failed to write output file `$filename'");
exit();
}
?>

View File

@@ -0,0 +1,14 @@
## Web server storage
It's possible to use the Dropbox functionality to store snapshots or video on your own server.
Look under Motion Settings»Motion Detection»Storage»Dropbox storage. There should be an option to change the Dropbox URL. This can be set to an internal or external web server. The default uses the Dropbox server.
Example: https[]()://yourserver/dafangstor.php
[dafangstor.php](dafangstor.php) is an example PHP script that can be used on your own server. Inside this file there are two options:
* APIKEY
* OUTDIR
APIKEY is the Long Lived Token value that is used to authenticate requests. This must match the value entered under Settings»Motion Detection»Storage»Dropbox storage»Dropbox Long Lived Token. The PHP script can be modified to include more fancy features like maybe for example changing the OUTDIR based on the authentication token or something similar.
OUTDIR sets the output directory relative to where PHP is running. The www user (eg. www-data) will need access permissions for this directory so that it can create sub directories and output files or you can manually create directories beforehand. Paths specified by "Dropbox snapshots remote directory" and "Dropbox videos remote directory" will be relative to this OUTDIR directory.