From 945787b286f17801faf513f3021461fc4a36ce0e Mon Sep 17 00:00:00 2001 From: Alexander Heinlein Date: Sun, 11 Jun 2017 17:14:19 +0200 Subject: [PATCH] Initial commit --- bot.py | 387 +++++++++++++++++++++++++++ config.json | 66 +++++ motion.conf | 745 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1198 insertions(+) create mode 100755 bot.py create mode 100644 config.json create mode 100644 motion.conf diff --git a/bot.py b/bot.py new file mode 100755 index 0000000..578eeca --- /dev/null +++ b/bot.py @@ -0,0 +1,387 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# dependencies: +# - https://github.com/python-telegram-bot/python-telegram-bot +# - https://github.com/dsoprea/PyInotify +# +# similar project: +# - https://github.com/FutureSharks/rpi-security/blob/master/bin/rpi-security.py +# +# - todo: +# - configurable log file path +# - introduce class, get rid of global variables +# - check return code of raspistill +# - support multiple owners +# + +import RPi.GPIO as GPIO +import inotify.adapters +import json +import logging +import logging.handlers +import os +import shlex +import shutil +import signal +import subprocess +import sys +import telegram +import threading +import time +import traceback +from telegram.error import NetworkError, Unauthorized + +# id for keeping track of the last seen message +update_id = None +# config from config file +config = None +# logging stuff +logger = None +# send capture images? +reportMotion = False + +def main(): + global config + global logger + global update_id + + # setup logging, we want to log both to stdout and a file + logFormat = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + logger = logging.getLogger(__name__) + fileHandler = logging.handlers.TimedRotatingFileHandler(filename='picam.log', when='D', backupCount=7) + fileHandler.setFormatter(logFormat) + logger.addHandler(fileHandler) + stdoutHandler = logging.StreamHandler(sys.stdout) + stdoutHandler.setFormatter(logFormat) + logger.addHandler(stdoutHandler) + logger.setLevel(logging.INFO) + + logger.info('Starting') + + config = json.load(open('config.json', 'r')) + # check for conflicting config options + if config['pir']['enable'] and config['motion']['enable']: + logger.error('Enabling both PIR and motion based capturing is not supported') + return + + bot = telegram.Bot(config['telegram']['token']) + + # check if API access works. try again on network errors, + # might happen after boot while the network is still being set up + logger.info('Waiting for network and API to become accessible...') + timeout = config['general']['startup_timeout'] + timeout = timeout if timeout > 0 else sys.maxint + for i in xrange(timeout): + try: + logger.info(bot.getMe()) + logger.info('API access working!') + break # success + except NetworkError as e: + pass # don't log, just ignore + except Exception as e: + # log other exceptions, then break + logger.error(e.message) + logger.error(traceback.format_exc()) + raise + time.sleep(1) + + # pretend to be nice to our owners + for owner_id in config['telegram']['owner_ids']: + try: + bot.sendMessage(chat_id=owner_id, text="Hello there, I'm back!") + except Exception as e: + # most likely network problem or user has blocked the bot + logger.warn('Could not send hello to user %s: %s' % (owner_id, e.message)) + + # get the first pending update_id, this is so we can skip over it in case + # we get an "Unauthorized" exception + try: + update_id = bot.getUpdates()[0].update_id + except IndexError: + update_id = None + + # set up telegram thread + telegram_thread = threading.Thread(target=fetchTelegramUpdates, args=[bot]) + telegram_thread.daemon = True + telegram_thread.start() + + # set up watch thread for captured images + image_watch_thread = threading.Thread(target=fetchImageUpdates, args=[bot]) + image_watch_thread.daemon = True + image_watch_thread.start() + + # set up PIR thread + if config['pir']['enable']: + pir_thread = threading.Thread(target=watchPIR) + pir_thread.daemon = True + pir_thread.start() + + while True: + time.sleep(0.1) + # TODO XXX FIXME check if all threads are still alive? + +def fetchTelegramUpdates(bot): + logger.info('Setting up telegram thread') + global update_id + while True: + try: + # request updates after the last update_id + # timeout: how long to poll for messages + for update in bot.getUpdates(offset=update_id, timeout=10): + # chat_id is required to reply to any message + chat_id = update.message.chat_id + update_id = update.update_id + 1 + + # skip updates without a message + if not update.message: + continue + + message = update.message + + # skip messages from non-owner + if message.from_user.id not in config['telegram']['owner_ids']: + logger.warn('Received message from unknown user "%s": "%s"' % (message.from_user, message.text)) + message.reply_text("I'm sorry, Dave. I'm afraid I can't do that.") + continue + + logger.info('Received message from user "%s": "%s"' % (message.from_user, message.text)) + performCommand(message) + except NetworkError as e: + time.sleep(1) + except Exception as e: + logger.warn(e.message) + logger.warn(traceback.format_exc()) + +def performCommand(message): + cmd = message.text.lower().rstrip() + if cmd == '/start': + # ignore default start command + return + if cmd == '/arm': + commandArm(message) + elif cmd == '/disarm': + commandDisarm(message) + elif cmd == 'kill': + commandKill(message) + elif cmd == '/status': + commandStatus(message) + elif cmd == '/capture': + stopStart = isMotionRunning() + if stopStart: + commandDisarm(message) + commandCapture(message) + if stopStart: + commandArm(message) + else: + logger.warn('Unknown command: "%s"' % message.text) + +def commandArm(message): + global reportMotion + if reportMotion: + message.reply_text('Motion-based capturing already enabled.') + return + + if not config['motion']['enable'] and not config['pir']['enable']: + message.reply_text('Error: Cannot enable motion-based capturing since neither PIR nor motion is enabled!') + return + + message.reply_text('Enabling motion-based capturing...') + reportMotion = True + + if not config['motion']['enable']: + # we are done, PIR needs no further steps + return + + # start motion software if not already running + if isMotionRunning(): + message.reply_text('Motion software already running.') + return + + args = shlex.split(config['motion']['cmd']) + subprocess.call(args) + + # wait until motion is running to prevent + # multiple start and wrong status reports + for i in range(10): + if isMotionRunning(): + message.reply_text('Motion software now running.') + return + time.sleep(1) + message.reply_text('Motion software still not running. Please check status later.') + +def commandDisarm(message): + global reportMotion + if not reportMotion: + message.reply_text('Motion-based capturing not enabled.') + return + + message.reply_text('Disabling motion-based capturing...') + reportMotion = False + + if not config['motion']['enable']: + return + + pid = getMotionPID() + if pid is None: + message.reply_text('No PID file found. Assuming motion software not running. If in doubt use "kill".') + return + + if not os.path.exists('/proc/%s' % pid): + message.reply_text('PID found but no corresponding proc entry. Removing PID file.') + os.remove(config['motion']['pid_file']) + return + + try: + os.kill(pid, signal.SIGTERM) + except OSError: + # ingore if already gone + pass + # wait for process to terminate, can take some time + for i in range(10): + if not os.path.exists('/proc/%s' % pid): + message.reply_text('Motion software has been stopped.') + return + time.sleep(1) + + message.reply_text("Could not terminate process. Trying to kill it...") + try: + os.kill(pid, signal.SIGKILL) + except OSError: + # ignore if already gone + pass + + # wait for process to terminate, can take some time + for i in range(10): + if not os.path.exists('/proc/%s' % pid): + message.reply_text('Motion software has been stopped.') + return + time.sleep(1) + message.reply_text('Error: Unable to stop motion software.') + +def commandKill(message): + if not config['motion']['enable']: + message.reply_text('Error: kill command only supported when motion is enabled') + return + args = shlex.split('killall -9 %s' % config['motion']['kill_name']) + subprocess.call(args) + message.reply_text('Kill signal has been sent.') + +def commandStatus(message): + if not reportMotion: + message.reply_text('Motion-based capturing not enabled.') + return + + image_dir = config['general']['image_dir'] + if not os.path.exists(image_dir): + message.reply_text('Error: Motion-based capturing enabled but image dir not available!') + return + + if config['motion']['enable']: + # check if motion software is running or died unexpectedly + if not isMotionRunning(): + message.reply_text('Error: Motion-based capturing enabled but motion software not running!') + return + message.reply_text('Motion-based capturing enabled and motion software running.') + else: + message.reply_text('Motion-based capturing enabled.') + +def commandCapture(message): + message.reply_text('Capture in progress, please wait...') + + capture_file = config['capture']['file'].encode('utf-8') + if os.path.exists(capture_file): + os.remove(capture_file) + + args = shlex.split(config['capture']['cmd']) + subprocess.call(args) + + if not os.path.exists(capture_file): + message.reply_text('Error: Capture file not found: "%s"' % capture_file) + return + + message.reply_photo(photo=open(capture_file, 'rb')) + if config['general']['delete_images']: + os.remove(capture_file) + +def fetchImageUpdates(bot): + logger.info('Setting up image watch thread') + + # set up image directory watch + watch_dir = config['general']['image_dir'] + # purge (remove and re-create) if we allowed to do so + if config['general']['delete_images']: + shutil.rmtree(watch_dir, ignore_errors=True) + if not os.path.exists(watch_dir): + os.makedirs(watch_dir) # racy but we don't care + notify = inotify.adapters.Inotify() + notify.add_watch(watch_dir) + + # check for new events + # (runs forever but we could bail out: check for event being None + # which always indicates the last event) + for event in notify.event_gen(): + if event is None: + continue + + (header, type_names, watch_path, filename) = event + + # only watch for created and renamed files + matched_types = ['IN_CLOSE_WRITE', 'IN_MOVED_TO'] + if not any(type in type_names for type in matched_types): + continue + + # check for image + filepath = ('%s/%s' % (watch_path, filename)).encode('utf-8') + if not filename.endswith('.jpg'): + logger.info('New non-image file: "%s" - ignored' % filepath) + continue + + logger.info('New image file: "%s"' % filepath) + if reportMotion: + for owner_id in config['telegram']['owner_ids']: + try: + bot.sendPhoto(chat_id=owner_id, caption=filepath, photo=open(filepath, 'rb')) + except Exception as e: + # most likely network problem or user has blocked the bot + logger.warn('Could not send image to user %s: %s' % (owner_id, e.message)) + + # always delete image, even if reporting is disabled + if config['general']['delete_images']: + os.remove(filepath) + +def getMotionPID(): + pid_file = config['motion']['pid_file'] + if not os.path.exists(pid_file): + return None + with open(pid_file, 'r') as f: + pid = f.read().rstrip() + return int(pid) + +def isMotionRunning(): + pid = getMotionPID() + return os.path.exists('/proc/%s' % pid) + +def watchPIR(): + logger.info('Setting up PIR watch thread') + + gpio = config['pir']['gpio'] + GPIO.setmode(GPIO.BOARD) + GPIO.setup(gpio, GPIO.IN) + while True: + pir = GPIO.input(gpio) + if pir == 0: + # no motion detected + time.sleep(1) + continue + + if not reportMotion: + time.sleep(1) + continue + + logger.info('PIR: motion detected') + args = shlex.split(config['pir']['capture_cmd']) + subprocess.call(args) + +if __name__ == '__main__': + main() diff --git a/config.json b/config.json new file mode 100644 index 0000000..0d44f13 --- /dev/null +++ b/config.json @@ -0,0 +1,66 @@ +{ + "//": "general settings", + "general": { + "//": "how long to wait after startup for the network & API to become accessible", + "//": "useful if started after boot while the network is still being set up", + "//": "value is in seconds, specify 0 to wait forever until access succeeds", + "startup_timeout": 0, + + "//": "directory where capture images will appear", + "//": "note: make sure that both the PIR capture command and the motion software", + "//": " write their images into exactly this directory!", + "image_dir": "/tmp/piCamBot", + + "//": "delete captured image after sending them? recommended, otherwise your disk runs full", + "//": "note: this also purges (removes and re-creates) the image directory on startup", + "delete_images": true + }, + + "//": "telegram-related settings", + "telegram": { + "//": "telegram bot authorization token", + "token": "YOUR_TELEGRAM_API_TOKEN", + + "//": "telegram IDs of the bot owner(s). only owners can interact with the bot", + "owner_ids": [ YOUR_TELEGRAM_USER_ID ] + }, + + "//": "PIR (passive infrared) sensor related settings (for motion detection via PIR sensor)", + "pir": { + "//": "enable motion detection via PIR sensor?", + "enable": true, + + "//": "GPIO pin for reading from sensor", + "gpio": 8, + + "//": "command for creating snapshots if motion is detected", + "capture_cmd": "raspistill -a 12 -dt -vf -hf -n -t 1500 -o /tmp/piCamBot/piCamBot_%d.jpg", + "__capture_cmd": "raspistill -a 12 -dt -vf -hf -n -t 5000 -tl 1000 -o /tmp/piCamBot/piCamBot_%d.jpg" + }, + + "//": "motion-related settings (for motion detection via camera)", + "motion": { + "//": "enable motion detection via camera?", + "enable": false, + + "//": "pid file for stopping the motion daemon (process_id_file setting)", + "pid_file": "/tmp/motion/motion.pid", + + "//": "command for starting motion (always use option -b for daemon mode!)", + "cmd": "motion -b -c /home/pi/picambot/motion.conf", + + "//": "process name to send SIGKILL to in case stopping by PID is not possible", + "kill_name": "motion" + }, + + "//": "settings for capturing single shots", + "capture": { + "//": "command for creating a snapshot", + "//": "note: the resulting image should be located outside the usual image_dir,", + "//": " otherwise it gets ignored while motion tracking is disabled", + "cmd": "raspistill -t 2000 -a 12 -vf -hf -n -o /tmp/piCamBot.jpg", + + "//": "where to find the image after capturing", + "file": "/tmp/piCamBot.jpg" + } +} diff --git a/motion.conf b/motion.conf new file mode 100644 index 0000000..c133387 --- /dev/null +++ b/motion.conf @@ -0,0 +1,745 @@ +############################################################ +# Daemon +############################################################ + +# Start in daemon (background) mode and release terminal (default: off) +daemon on + +# File to store the process ID, also called pid file. (default: not defined) +process_id_file /tmp/motion/motion.pid + +############################################################ +# Basic Setup Mode +############################################################ + +# Start in Setup-Mode, daemon disabled. (default: off) +setup_mode off + + +# Use a file to save logs messages, if not defined stderr and syslog is used. (default: not defined) +;logfile /tmp/motion.log + +# Level of log messages [1..9] (EMR, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL). (default: 6 / NTC) +log_level 6 + +# Filter to log messages by type (COR, STR, ENC, NET, DBL, EVT, TRK, VID, ALL). (default: ALL) +log_type all + +########################################################### +# Capture device options +############################################################ + +# Videodevice to be used for capturing (default /dev/video0) +# for FreeBSD default is /dev/bktr0 +#videodevice /dev/video0 + +# v4l2_palette allows to choose preferable palette to be use by motion +# to capture from those supported by your videodevice. (default: 17) +# E.g. if your videodevice supports both V4L2_PIX_FMT_SBGGR8 and +# V4L2_PIX_FMT_MJPEG then motion will by default use V4L2_PIX_FMT_MJPEG. +# Setting v4l2_palette to 2 forces motion to use V4L2_PIX_FMT_SBGGR8 +# instead. +# +# Values : +# V4L2_PIX_FMT_SN9C10X : 0 'S910' +# V4L2_PIX_FMT_SBGGR16 : 1 'BYR2' +# V4L2_PIX_FMT_SBGGR8 : 2 'BA81' +# V4L2_PIX_FMT_SPCA561 : 3 'S561' +# V4L2_PIX_FMT_SGBRG8 : 4 'GBRG' +# V4L2_PIX_FMT_SGRBG8 : 5 'GRBG' +# V4L2_PIX_FMT_PAC207 : 6 'P207' +# V4L2_PIX_FMT_PJPG : 7 'PJPG' +# V4L2_PIX_FMT_MJPEG : 8 'MJPEG' +# V4L2_PIX_FMT_JPEG : 9 'JPEG' +# V4L2_PIX_FMT_RGB24 : 10 'RGB3' +# V4L2_PIX_FMT_SPCA501 : 11 'S501' +# V4L2_PIX_FMT_SPCA505 : 12 'S505' +# V4L2_PIX_FMT_SPCA508 : 13 'S508' +# V4L2_PIX_FMT_UYVY : 14 'UYVY' +# V4L2_PIX_FMT_YUYV : 15 'YUYV' +# V4L2_PIX_FMT_YUV422P : 16 '422P' +# V4L2_PIX_FMT_YUV420 : 17 'YU12' +# +v4l2_palette 17 + +# Tuner device to be used for capturing using tuner as source (default /dev/tuner0) +# This is ONLY used for FreeBSD. Leave it commented out for Linux +; tunerdevice /dev/tuner0 + +# The video input to be used (default: -1) +# Should normally be set to 0 or 1 for video/TV cards, and -1 for USB cameras +input -1 + +# The video norm to use (only for video capture and TV tuner cards) +# Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) +norm 0 + +# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0) +frequency 0 + +# Rotate image this number of degrees. The rotation affects all saved images as +# well as movies. Valid values: 0 (default = no rotation), 90, 180 and 270. +rotate 180 + +# note: although the raspi camera v2 can record 1080p only up to 720 will be +# possible here, probably due to using v4l2 instead of mmal? +# working modes: +# 1280x720 +# 1024x576 +# 640x480 + +# Image width (pixels). Valid range: Camera dependent, default: 352 +width 1280 +#width 1024 +#width 640 + +# Image height (pixels). Valid range: Camera dependent, default: 288 +height 720 +#height 576 +#height 480 + +# Maximum number of frames to be captured per second. +# Valid range: 2-100. Default: 100 (almost no limit). +framerate 1 + +# Minimum time in seconds between capturing picture frames from the camera. +# Default: 0 = disabled - the capture rate is given by the camera framerate. +# This option is used when you want to capture images at a rate lower than 2 per second. +minimum_frame_time 2 + +# URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// mjpg:// rstp:// or file:///) +# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined +; netcam_url value + +# Username and password for network camera (only if required). Default: not defined +# Syntax is user:password +; netcam_userpass value + +# The setting for keep-alive of network socket, should improve performance on compatible net cameras. +# off: The historical implementation using HTTP/1.0, closing the socket after each http request. +# force: Use HTTP/1.0 requests with keep alive header to reuse the same connection. +# on: Use HTTP/1.1 requests that support keep alive as default. +# Default: off +netcam_keepalive off + +# URL to use for a netcam proxy server, if required, e.g. "http://myproxy". +# If a port number other than 80 is needed, use "http://myproxy:1234". +# Default: not defined +; netcam_proxy value + +# Set less strict jpeg checks for network cameras with a poor/buggy firmware. +# Default: off +netcam_tolerant_check off + +# Let motion regulate the brightness of a video device (default: off). +# The auto_brightness feature uses the brightness option as its target value. +# If brightness is zero auto_brightness will adjust to average brightness value 128. +# Only recommended for cameras without auto brightness +auto_brightness off + +# Set the initial brightness of a video device. +# If auto_brightness is enabled, this value defines the average brightness level +# which Motion will try and adjust to. +# Valid range 0-255, default 0 = disabled +brightness 0 +#brightness 170 + +# Set the contrast of a video device. +# Valid range 0-255, default 0 = disabled +contrast 0 +#contrast 128 + +# Set the saturation of a video device. +# Valid range 0-255, default 0 = disabled +saturation 0 +#saturation 128 + +# Set the hue of a video device (NTSC feature). +# Valid range 0-255, default 0 = disabled +hue 0 + +############################################################ +# Round Robin (multiple inputs on same video device name) +############################################################ + +# Number of frames to capture in each roundrobin step (default: 1) +roundrobin_frames 1 + +# Number of frames to skip before each roundrobin step (default: 1) +roundrobin_skip 1 + +# Try to filter out noise generated by roundrobin (default: off) +switchfilter off + + +############################################################ +# Motion Detection Settings: +############################################################ + +# tests: +# low threshold (4608), medium noise_level (64) -> lots of false positives when facing bright window (up to 20k changed pixels) +# low threshold (4608), high noise_level (96) -> ?? + +# Threshold for number of changed pixels in an image that +# triggers motion detection (default: 1500) +threshold 4608 # 1280*720*0.005 +#threshold 9216 # 1280*720*0.01 + +# Automatically tune the threshold down if possible (default: off) +threshold_tune off + +# Noise threshold for the motion detection (default: 32) +noise_level 96 + +# Automatically tune the noise threshold (default: on) +noise_tune off + +# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined) +# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid. +# (l)abeling must only be used once and the 'l' must be the last letter. +# Comment out to disable +despeckle_filter EedDl + +# Detect motion in predefined areas (1 - 9). Areas are numbered like that: 1 2 3 +# A script (on_area_detected) is started immediately when motion is 4 5 6 +# detected in one of the given areas, but only once during an event. 7 8 9 +# One or more areas can be specified with this option. Take care: This option +# does NOT restrict detection to these areas! (Default: not defined) +; area_detect value + +# PGM file to use as a sensitivity mask. +# Full path name to. (Default: not defined) +; mask_file value + +# Dynamically create a mask file during operation (default: 0) +# Adjust speed of mask changes from 0 (off) to 10 (fast) +smart_mask_speed 0 + +# Ignore sudden massive light intensity changes given as a percentage of the picture +# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled +lightswitch 0 + +# Picture frames must contain motion at least the specified number of frames +# in a row before they are detected as true motion. At the default of 1, all +# motion is detected. Valid range: 1 to thousands, recommended 1-5 +minimum_motion_frames 1 + +# Specifies the number of pre-captured (buffered) pictures from before motion +# was detected that will be output at motion detection. +# Recommended range: 0 to 5 (default: 0) +# Do not use large values! Large values will cause Motion to skip video frames and +# cause unsmooth movies. To smooth movies use larger values of post_capture instead. +pre_capture 0 + +# Number of frames to capture after motion is no longer detected (default: 0) +post_capture 0 + +# Event Gap is the seconds of no motion detection that triggers the end of an event. +# An event is defined as a series of motion images taken within a short timeframe. +# Recommended value is 60 seconds (Default). The value -1 is allowed and disables +# events causing all Motion to be written to one single movie file and no pre_capture. +# If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An +# event ends right after no more motion is detected and post_capture is over. +event_gap 60 + +# Maximum length in seconds of a movie +# When value is exceeded a new movie file is created. (Default: 0 = infinite) +max_movie_time 0 + +# Always save images even if there was no motion (default: off) +emulate_motion off + + +############################################################ +# Image File Output +############################################################ + +# Output 'normal' pictures when motion is detected (default: on) +# Valid values: on, off, first, best, center +# When set to 'first', only the first picture of an event is saved. +# Picture with most motion of an event is saved when set to 'best'. +# Picture with motion nearest center of picture is saved when set to 'center'. +# Can be used as preview shot for the corresponding movie. +output_pictures on + +# Output pictures with only the pixels moving object (ghost images) (default: off) +output_debug_pictures off + +# The quality (in percent) to be used by the jpeg compression (default: 75) +quality 90 + +# Type of output images +# Valid values: jpeg, ppm (default: jpeg) +picture_type jpeg + +############################################################ +# FFMPEG related options +# Film (movies) file output, and deinterlacing of the video input +# The options movie_filename and timelapse_filename are also used +# by the ffmpeg feature +############################################################ + +# Use ffmpeg to encode movies in realtime (default: off) +ffmpeg_output_movies off + +# Use ffmpeg to make movies with only the pixels moving +# object (ghost images) (default: off) +ffmpeg_output_debug_movies off + +# Use ffmpeg to encode a timelapse movie +# Default value 0 = off - else save frame every Nth second +ffmpeg_timelapse 0 + +# The file rollover mode of the timelapse video +# Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual +ffmpeg_timelapse_mode daily + +# Bitrate to be used by the ffmpeg encoder (default: 400000) +# This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled) +ffmpeg_bps 500000 + +# Enables and defines variable bitrate for the ffmpeg encoder. +# ffmpeg_bps is ignored if variable bitrate is enabled. +# Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, +# or the range 2 - 31 where 2 means best quality and 31 is worst. +ffmpeg_variable_bitrate 0 + +# Codec to used by ffmpeg for the video compression. +# Timelapse mpegs are always made in mpeg1 format independent from this option. +# Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4. +# mpeg1 - gives you files with extension .mpg +# mpeg4 or msmpeg4 - gives you files with extension .avi +# msmpeg4 is recommended for use with Windows Media Player because +# it requires no installation of codec on the Windows client. +# swf - gives you a flash film with extension .swf +# flv - gives you a flash video with extension .flv +# ffv1 - FF video codec 1 for Lossless Encoding ( experimental ) +# mov - QuickTime ( testing ) +# ogg - Ogg/Theora ( testing ) +ffmpeg_video_codec mpeg4 + +# Use ffmpeg to deinterlace video. Necessary if you use an analog camera +# and see horizontal combing on moving objects in video or pictures. +# (default: off) +ffmpeg_deinterlace off + +############################################################ +# SDL Window +############################################################ + +# Number of motion thread to show in SDL Window (default: 0 = disabled) +#sdl_threadnr 0 + +############################################################ +# External pipe to video encoder +# Replacement for FFMPEG builtin encoder for ffmpeg_output_movies only. +# The options movie_filename and timelapse_filename are also used +# by the ffmpeg feature +############################################################# + +# Bool to enable or disable extpipe (default: off) +use_extpipe off + +# External program (full path and opts) to pipe raw video to +# Generally, use '-' for STDIN... +;extpipe mencoder -demuxer rawvideo -rawvideo w=320:h=240:i420 -ovc x264 -x264encopts bframes=4:frameref=1:subq=1:scenecut=-1:nob_adapt:threads=1:keyint=1000:8x8dct:vbv_bufsize=4000:crf=24:partitions=i8x8,i4x4:vbv_maxrate=800:no-chroma-me -vf denoise3d=16:12:48:4,pp=lb -of avi -o %f.avi - -fps %fps + + + +############################################################ +# Snapshots (Traditional Periodic Webcam File Output) +############################################################ + +# Make automated snapshot every N seconds (default: 0 = disabled) +snapshot_interval 0 + + +############################################################ +# Text Display +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, %T = HH:MM:SS, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, \n = new line, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event - do not use with text_event! +# You can put quotation marks around the text to allow +# leading spaces +############################################################ + +# Locate and draw a box around the moving object. +# Valid values: on, off, preview (default: off) +# Set to 'preview' will only draw a box in preview_shot pictures. +locate_motion_mode on + +# Set the look and style of the locate box if enabled. +# Valid values: box, redbox, cross, redcross (default: box) +# Set to 'box' will draw the traditional box. +# Set to 'redbox' will draw a red box. +# Set to 'cross' will draw a little cross to mark center. +# Set to 'redcross' will draw a little red cross to mark center. +locate_motion_style box + +# Draws the timestamp using same options as C function strftime(3) +# Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock +# Text is placed in lower right corner +text_right %Y-%m-%d\n%T-%q + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +; text_left CAMERA %t + +# Draw the number of changed pixed on the images (default: off) +# Will normally be set to off except when you setup and adjust the motion settings +# Text is placed in upper right corner +text_changes on + +# This option defines the value of the special event conversion specifier %C +# You can use any conversion specifier in this option except %C. Date and time +# values are from the timestamp of the first image in the current event. +# Default: %Y%m%d%H%M%S +# The idea is that %C can be used filenames and text_left/right for creating +# a unique identifier for each event. +text_event %Y%m%d%H%M%S + +# Draw characters at twice normal size on images. (default: off) +text_double off + + +# Text to include in a JPEG EXIF comment +# May be any text, including conversion specifiers. +# The EXIF timestamp is included independent of this text. +;exif_text %i%J/%K%L + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, picture_, movie_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute path. (Default: current working directory) +target_dir /tmp/piCamBot + +# File path for snapshots (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-snapshot +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot +# File extension .jpg or .ppm is automatically added so do not include this. +# Note: A symbolic link called lastsnap.jpg created in the target_dir will always +# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' +snapshot_filename %v-%Y%m%d%H%M%S-snapshot + +# File path for motion triggered images (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-%q +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q +# File extension .jpg or .ppm is automatically added so do not include this +# Set to 'preview' together with best-preview feature enables special naming +# convention for preview shots. See motion guide for details +picture_filename %v-%Y%m%d%H%M%S-%q + +# File path for motion triggered ffmpeg films (movies) relative to target_dir +# Default: %v-%Y%m%d%H%M%S +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S +# File extension .mpg or .avi is automatically added so do not include this +# This option was previously called ffmpeg_filename +movie_filename %v-%Y%m%d%H%M%S + +# File path for timelapse movies relative to target_dir +# Default: %Y%m%d-timelapse +# Default value is near equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse +# File extension .mpg is automatically added so do not include this +timelapse_filename %Y%m%d-timelapse + +############################################################ +# Global Network Options +############################################################ +# Enable or disable IPV6 for http control and stream (default: off ) +ipv6_enabled off + +############################################################ +# Live Stream Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +stream_port 8081 + +# Quality of the jpeg (in percent) images produced (default: 50) +stream_quality 50 + +# Output frames at 1 fps when no motion is detected and increase to the +# rate given by stream_maxrate when motion is detected (default: off) +stream_motion off + +# Maximum framerate for stream streams (default: 1) +stream_maxrate 1 + +# Restrict stream connections to localhost only (default: on) +stream_localhost off + +# Limits the number of images per connection (default: 0 = unlimited) +# Number can be defined by multiplying actual stream rate by desired number of seconds +# Actual stream rate is the smallest of the numbers framerate and stream_maxrate +stream_limit 0 + +# Set the authentication method (default: 0) +# 0 = disabled +# 1 = Basic authentication +# 2 = MD5 digest (the safer authentication) +stream_auth_method 0 + +# Authentication for the stream. Syntax username:password +# Default: not defined (Disabled) +; stream_authentication username:password + + +############################################################ +# HTTP Based Control +############################################################ + +# TCP/IP port for the http server to listen on (default: 0 = disabled) +webcontrol_port 8080 + +# Restrict control connections to localhost only (default: on) +webcontrol_localhost on + +# Output for http server, select off to choose raw text plain (default: on) +webcontrol_html_output on + +# Authentication for the http based control. Syntax username:password +# Default: not defined (Disabled) +; webcontrol_authentication username:password + + +############################################################ +# Tracking (Pan/Tilt) +############################################################# + +# Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic, 5=uvcvideo, 6=servo) +# The generic type enables the definition of motion center and motion size to +# be used with the conversion specifiers for options like on_motion_detected +track_type 0 + +# Enable auto tracking (default: off) +track_auto off + +# Serial port of motor (default: none) +;track_port /dev/ttyS0 + +# Motor number for x-axis (default: 0) +;track_motorx 0 + +# Set motorx reverse (default: 0) +;track_motorx_reverse 0 + +# Motor number for y-axis (default: 0) +;track_motory 1 + +# Set motory reverse (default: 0) +;track_motory_reverse 0 + +# Maximum value on x-axis (default: 0) +;track_maxx 200 + +# Minimum value on x-axis (default: 0) +;track_minx 50 + +# Maximum value on y-axis (default: 0) +;track_maxy 200 + +# Minimum value on y-axis (default: 0) +;track_miny 50 + +# Center value on x-axis (default: 0) +;track_homex 128 + +# Center value on y-axis (default: 0) +;track_homey 128 + +# ID of an iomojo camera if used (default: 0) +track_iomojo_id 0 + +# Angle in degrees the camera moves per step on the X-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_x 10 + +# Angle in degrees the camera moves per step on the Y-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_y 10 + +# Delay to wait for after tracking movement as number +# of picture frames (default: 10) +track_move_wait 10 + +# Speed to set the motor to (stepper motor option) (default: 255) +track_speed 255 + +# Number of steps to make (stepper motor option) (default: 40) +track_stepsize 40 + + +############################################################ +# External Commands, Warnings and Logging: +# You can use conversion specifiers for the on_xxxx commands +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# %f = filename with full path +# %n = number indicating filetype +# Both %f and %n are only defined for on_picture_save, +# on_movie_start and on_movie_end +# Quotation marks round string are allowed. +############################################################ + +# Do not sound beeps when detecting motion (default: on) +# Note: Motion never beeps when running in daemon mode. +quiet on + +# Command to be executed when an event starts. (default: none) +# An event starts at first motion detected after a period of no motion defined by event_gap +; on_event_start value + +# Command to be executed when an event ends after a period of no motion +# (default: none). The period of no motion is defined by option event_gap. +; on_event_end value + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# To give the filename as an argument to a command append it with %f +; on_picture_save value + +# Command to be executed when a motion frame is detected (default: none) +; on_motion_detected value + +# Command to be executed when motion in a predefined area is detected +# Check option 'area_detect'. (default: none) +; on_area_detected value + +# Command to be executed when a movie file (.mpg|.avi) is created. (default: none) +# To give the filename as an argument to a command append it with %f +; on_movie_start value + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# To give the filename as an argument to a command append it with %f +; on_movie_end value + +# Command to be executed when a camera can't be opened or if it is lost +# NOTE: There is situations when motion don't detect a lost camera! +# It depends on the driver, some drivers dosn't detect a lost camera at all +# Some hangs the motion thread. Some even hangs the PC! (default: none) +; on_camera_lost value + +##################################################################### +# Common Options for database features. +# Options require database options to be active also. +##################################################################### + +# Log to the database when creating motion triggered picture file (default: on) +; sql_log_picture on + +# Log to the database when creating a snapshot image file (default: on) +; sql_log_snapshot on + +# Log to the database when creating motion triggered movie file (default: off) +; sql_log_movie off + +# Log to the database when creating timelapse movies file (default: off) +; sql_log_timelapse off + +# SQL query string that is sent to the database +# Use same conversion specifiers has for text features +# Additional special conversion specifiers are +# %n = the number representing the file_type +# %f = filename with full path +# Default value: +# Create tables : +## +# Mysql +# CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), event_time_stamp timestamp(14)); +# +# Postgresql +# CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp without time zone, event_time_stamp timestamp without time zone); +# +# insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +; sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') + + +############################################################ +# Database Options +############################################################ + +# database type : mysql, postgresql, sqlite3 (default : not defined) +; database_type value + +# database to log to (default: not defined) +; database_dbname value + +# The host on which the database is located (default: localhost) +; database_host value + +# User account name for database (default: not defined) +; database_user value + +# User password for database (default: not defined) +; database_password value + +# Port on which the database is located +# mysql 3306 , postgresql 5432 (default: not defined) +; database_port value + +############################################################ +# Database Options For SQLite3 +############################################################ + +# SQLite3 database (file path) (default: not defined) +; sqlite3_db value + + + +############################################################ +# Video Loopback Device (vloopback project) +############################################################ + +# Output images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; video_pipe value + +# Output motion images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; motion_video_pipe value + + +############################################################## +# Thread config files - One for each camera. +# Except if only one camera - You only need this config file. +# If you have more than one camera you MUST define one thread +# config file for each camera in addition to this config file. +############################################################## + +# Remember: If you have more than one camera you must have one +# thread file for each camera. E.g. 2 cameras requires 3 files: +# This motion.conf file AND thread1.conf and thread2.conf. +# Only put the options that are unique to each camera in the +# thread config files. +; thread /etc/motion/thread1.conf +; thread /etc/motion/thread2.conf +; thread /etc/motion/thread3.conf +; thread /etc/motion/thread4.conf +