I tried to get zomeminder running. It works somehow but it is actually a mess.

Zoneminder is an old crufty software written in a bizzare mix of languages, not the right thing for some simple motion detection and recording.

There is a much better tool: motion.

Here are the docs

After setting it up, you can go to http://localhost:5050 and get a webinterface showing live streams. When motion is detected, videos are stored.

You can clean up with cron using something like

find -type f -name '*.mkv' -mtime +7 -delete && find -type d -empty -delete

Tracking is a bit tricky #937.

Setup & Config

Getting motion to run in a Docker container, simple build an images with

FROM alpine
RUN apk upgrade --no-cache && apk add --no-cache bash curl
RUN apk add --no-cache tzdata && ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime && echo "Europe/Berlin" >  /etc/timezone
RUN apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/testing motion
WORKDIR /data
CMD ["motion"]

and create a container with (I like to use docker-compose for reproducability)

version: "3"

services:
  motion:
    build:
      context: .
    restart: unless-stopped
    healthcheck:
      test: 'curl -sf http://localhost:5050/0/detection/connection'
    ports:
      - "5050-5051:5050-5051"
    volumes:
      - "./:/data"

I configured motion this way

# motion detection
framerate 5
noise_level 32
noise_tune on
despeckle_filter EedDl
#smart_mask_speed 5
threshold 1500
threshold_maximum 40000
#threshold_tune on
lightswitch_percent 90
locate_motion_mode on
locate_motion_style redbox
pre_capture 5
post_capture 10
event_gap 30
text_changes on
text_event E%v D%D
text_left cam%t %C
#target_dir motion

# movie output
movie_output on
#movie_quality 80
movie_filename motion/%Y%m%d/%Y%m%dT%H%M%S-%v-cam%t-motion

# picture output
picture_output off
#picture_output_motion on
#picture_quality 80
picture_filename motion/%Y%m%dT%H%M%S-%v-cam%t-motion

# timelapse
timelapse_interval 5
timelapse_filename timelapse/%Y%m%d-cam%t-timelapse

# snapshots
snapshot_interval 0
snapshot_filename snapshots/%Y%m%dT%H%M%S-cam%t-snapshot

# web interface
webcontrol_port 5050
webcontrol_ipv6 on
webcontrol_localhost off
webcontrol_parms 1
#webcontrol_interface 2

# stream
stream_port 5051
stream_localhost off
stream_maxrate 5
stream_motion on
stream_preview_method 3
stream_quality 85
#stream_preview_scale 50

netcam_url http://cam1/video.cgi

track_auto on
track_type 4
#track_move_wait 0
#threshold_maximum 0
track_move_wait 60
post_capture 60
track_generic_move /data/track %t %D >>motion.log

With the tracking script

#!/bin/bash

URL="http://cam1/pantiltcontrol.cgi"
PPD=12 # pixels per degree [px/°]
MM=10 # min required move distance [°]
MARKER=moved
WAIT=15


#date -Is
#echo "$@"
#env |grep TRACK_

# moves
# 0 1 2
# 3 4 5
# 6 7 8

move(){
  PAN=$1
  TILT=$2
  [[ $PAN < 0 && $TILT > 0 ]] && { DIR=0; A='↖'; }
  [[ $PAN = 0 && $TILT > 0 ]] && { DIR=1; A='↑'; }
  [[ $PAN > 0 && $TILT > 0 ]] && { DIR=2; A='↗'; }
  [[ $PAN < 0 && $TILT = 0 ]] && { DIR=3; A='←'; }
  [[ $PAN = 0 && $TILT = 0 ]] && { DIR=4; A='·'; }
  [[ $PAN > 0 && $TILT = 0 ]] && { DIR=5; A='→'; }
  [[ $PAN < 0 && $TILT < 0 ]] && { DIR=6; A='↙'; }
  [[ $PAN = 0 && $TILT < 0 ]] && { DIR=7; A='↓'; }
  [[ $PAN > 0 && $TILT < 0 ]] && { DIR=8; A='↘'; }
  PAN=${PAN#-}
  TILT=${TILT#-}
  echo $(date -Is) "MOVE pan=$PAN tilt=$TILT dir=$DIR $A ($1,$2)"
  curl -sf -G "$URL" --user admin:admin --data PanTiltSingleMove=$DIR --data PanSingleMoveDegree=$PAN --data TiltSingleMoveDegree=$TILE
  touch $MARKER
}

case $TRACK_ACTION in
  center)
    move 0 0
    ;;
  move)
    if [[ -f $MARKER ]]; then
      AGE=$(($(date +%s)-$(date +%s -r $MARKER)))
    #  echo $(date -Is) "AGE=$AGE"
      (( $AGE < $WAIT )) && exit 1
    fi

    IW=$TRACK_IMGS_WIDTH
    IH=$TRACK_IMGS_HEIGHT
    X=$TRACK_CENT_X
    Y=$TRACK_CENT_Y
    (( p=(X-IW/2)/$PPD ))
    (( t=-(Y-IH/2)/$PPD ))
    (( p*p+t*t>MM*MM )) && move $p $t #|| echo $(date -Is) "MOVE too small p=$p t=$t X=$X Y=$Y"
    ;;
esac