Search

The Big Browser

M y    b r a i n    h u r t s  !                                           w e                 r e a l l y                 t h i n k                   w h a t                y o u             k n o w

21 September 2024

Configuring motion as a live surveillance camera with telegram notifications on motion detected

Motion is a program that monitors the video signal from one or more cameras and can trigger a custom action when it detects that a picture has changed. It supports a wide variety of cameras, including generic USB cameras, the official rasperry pi camera as well compatible CSI camera modules that can be plugged into the RPI CSI slot that can be found on aliexpress.

Installing motion

On ubuntu/debian/raspios motion can be installed with:
sudo apt install motion

Once installed a configuration file will be created at /etc/motion/motion.conf

A minimal working configuration that defines two action hooks on motion detected — the hooks are used to post live video updates to your telegram chat (requires registering a telegram bot with the telegram botfather — see more on this below):


auto_brightness off
video_params input=-1 brightness=0 contrast=0 frequency=0 hue=0 saturation=0 power_line_frequency=-1


# !!!IMPORTANT: this sets the service to forking mode, 
# when `on` you MUST use `Type=forking` in the systemd unit file 
# located by default at /lib/systemd/system/motion.service
# if you use `daemon off` then set `Type=simple` in motion.service
daemon on
despeckle_filter EedDl
emulate_motion off
event_gap 0
movie_bps 400000
movie_duplicate_frames false

# IMPORTANT: set to off to disable CONTINUOUS movie recording, 
# this will still allow creating movies on motion detected 
movie_output_motion off

# IMPORTANT: enable movie creation on motion detected
movie_output on
timelapse_interval 0
timelapse_mode daily
movie_quality 0
movie_codec mp4
framerate 100

# resolution of recorded video
height 720
width 1280

webcontrol_ipv6 off
lightswitch_percent 0
locate_motion_mode off
locate_motion_style box
log_file /tmp/motion.log
log_level 6
log_type all
movie_max_time 0
minimum_frame_time 0
minimum_motion_frames 2
netcam_params keepalive=off tolerant_check=on rtsp_uses_tcp=on
noise_level 32
noise_tune on

# Hook to trigger when motion was detected; use to send a text message
on_event_start /usr/local/bin/tg_send_alert_curl.sh WARNING

# Hook to trigger when a movie has finished recording `%f` will pass the video file name
on_movie_end /usr/local/bin/tg_send_video_curl.sh %f

# on = motion will save a snapshot of the video feed every time motion is detected; 
# the images are stored in target_dir
picture_output_motion off

# on = motion will save snapshots at specified intervals, regardless of whether motion is detected; 
# the frequency of these snapshots is controlled by the `snapshot_interval` parameter
picture_output off

picture_type jpeg
post_capture 5
pre_capture 0
pid_file /tmp/motion.pid
picture_quality 75

# only log errors and warnings
quiet on

# controls rotation, possible values: 0, 90, 180, 270
rotate 0
roundrobin_frames 1
roundrobin_skip 1
roundrobin_switchfilter off
smart_mask_speed 0
snapshot_interval 0
stream_auth_method 0
stream_limit 0
stream_localhost off
stream_maxrate 100
stream_motion off
stream_port 2017
stream_quality 100
target_dir /media/motion
text_changes off
text_scale off
text_event %Y%m%d%H%M%S
text_right %Y-%m-%d\n%T-%q
threshold 1500
threshold_tune off
track_auto off
track_iomojo_id 0
track_move_wait 10
track_speed 255
track_step_angle_x 10
track_step_angle_y 10
track_stepsize 40
track_type 0
movie_extpipe_use off
video_device /dev/video0
webcontrol_interface on
webcontrol_localhost off
webcontrol_port 8080

👉 see Motion Configuration

Telegram hooks

To use telegram hooks you will first need to create a telegram bot:
  • open telegram.me/BotFather from telegram and send /newbot as a command to the bot and follow the steps. Once you assign a name and a username to your bot you'll get the bot token that will will look like 5432101234:XXXXXYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZ where 5432101234 is your bot ID and XXXXXYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZ is the SECRET TOKEN that you must not ever share with anyone!!! Make sure you save the bot token in a secure place.
  • To get help on what telegram.me/BotFather can do enter /help in the chat window.
Next, create a telegram group from the app, click the top bar, select Edit, click the Plus button , and add your bot by specifying the bot name or username. Now for the tricky part:
  1. Send any message to the group you have just created where your bot is already a member.
  2. Open the following URL in your browser: https://api.telegram.org/bot5432101234:XXXXXYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZ/getUpdates (replace with your BOT_TOKEN!)
  3. In the JSON response locate the chat and under it the id subnode which will be a negative number. Please note down this chat id number because you will need to use it in your telegram hooks!

    Let's assume the chat id value you got is -999999999 — this is the identifier for the group you created.

Now it's time to create the two hook files that you specified in your /etc/motion/motion.conf:

# /usr/local/bin/tg_send_alert_curl.sh
curl -X POST "https://api.telegram.org/bot5432101234:XXXXXYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZ/sendMessage" -F chat_id="-999999999" -F text="$1: motion detected $(date '+%d-%b-%Y %H:%M:%S')"

👉 see https://core.telegram.org/bots/api#sendmessage

To enable markdown in your messages add -F parse_mode=MarkdownV2 to the command above.
For HTML mode use -F parse_mode=HTML — be aware than only a subset of HTML is supported.


# /usr/local/bin/tg_send_video_curl.sh
curl -X POST "https://api.telegram.org/bot5432101234:XXXXXYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZ/sendVideo" -F chat_id="-999999999" -F video="@$1" -F caption="at $(date '+%Y-%b-%d %H:%M:%S')"

👉 see https://core.telegram.org/bots/api#sendvideo

The two files must be made executable:
sudo chmod +x /usr/local/bin/tg_send_alert_curl.sh
sudo chmod +x /usr/local/bin/tg_send_video_curl.sh

⚠️ Important notes

The bot prefix right bebore your BOT_TOKEN is required — make sure it is there or otherwise the command will fail!

The $(date '+%d-%b-%Y %H:%M:%S') snippet generates a timestamp that will appear in your telegram notifications — feel free to remove it if you don't need it.

The $1 parameter in the first and second script will take the value passed in from the motion config file at /etc/motion/motion.conf — in case of tg_send_video_curl.sh this will contain the path of the recorded video file.

⚠️ Before modifying motion.conf and the systemd service file make sure you stop the motion service:
sudo systemctl stop motion
Once you updated the configs restart the motion service with:
sudo systemct restart motion
See the current status of the motion service:
sudo systemctl status motion
Display live logs (assumes you have log_file /tmp/motion.log in your /etc/motion/motion.conf):
tail -f /tmp/motion.log

27 March 2021

Bulk convert APE (MonkeyAudio) files to FLAC recursively from command line

A oneliner that will search recursively for all .APE files under the current folder (ie .) and convert them to .FLAC. This will handle correctly file names containing special characters such as single and double quotes, brackets, etc.

find . -type f -iname "*.ape" -exec sh -c 'ffmpeg -i "${1}" "${1%.*}.flac"' _ {} \;
This approach can be used to convert between any format supported by ffmpeg, for exaple convert all FLAC files to MP3:

find . -type f -iname "*.flac" -exec sh -c 'ffmpeg -i "${1}" "${1%.*}.mp3"' _ {} \;

29 November 2020

Convert all MOV video files in the current folder to MP4

Convert a single MOV video file to MP4 format:

ffmpeg -i my_video.mov my_video.mp4
Convert all MOV videos in the current folder and sub-folders to MP4 and delete the orignal .MOV files: For Linux or Mac, or on Windows under msys, cygwin, cmder etc.
for video in `find . -iname "*.mov"` ; do
    ffmpeg -i "${video}" "${video%.*}.mp4" && rm "${video}"
done
NOTE: While the script with simple name.ext file names, it is likely to fail on files containing special characters, such as spaces, single or double quotes, slashes, dashes or !#&, etc. A more robust version that will handle correctly special characters in file names:
find . -iname '*.mov' -print0 | xargs -0 -I {} ffmpeg -i '{}' '{}.mp4' && rm '{}'
And another version of the same that avoids the use of xargs by using a sub-shell:
find . -type f -iname "*.mov" -exec sh -c 'ffmpeg -i "${1}" "${1%.*}.mp4" && rm "${1}" ' _ {} \;
If ffmpeg is not present on your system install it with apt install ffmpeg (on Linux) or brew install ffmpeg on Mac.

22 November 2020

CSS Style Sheet HTML Preview Generator (python script)

How do I see an HTML preview of every single CSS rule defined in a CSS file?

There is no fixed way to do it, especially if your CSS style sheets make abundant use of absolute and fixed positioning. This script below is a quick and dirty attempt to generate HTML tags for all the rules defined in a CSS file. If the rule is defined for a known HTML tag then that tag is rendered as itself, otherwise a div tag is created.

The script does not cover every possible scenario and only supports the most common CSS constructs. It wraps all fixed CSS rules into a dedicated iframe in order to prevent elements from overlapping. You can disable wrapping fixed elements into frames by passing --no-frames.

If you have a large CSS file the HTML generation can take up to several minutes since nested rules are iterated recursively.

⚠️ For complex CSS style sheets (1MB+) the generated HTML can cause the browser to freeze or rendering to take a signficant time even on a performant computer. You have been warned!

Dependencies

The CSS preview generator depends on the cssutil python module which can be installed with:

pip3 install cssutils

You are welcome to fork, improve and provide feedback.

Usage

css_stylesheet_preview_generator.py mystyles.css -o mypreview.html
For more options run with the -h or --help switch.

Example output

Python CSS preview generator script

16 October 2017

Barcelona city bikes availability: a console REST client with curl and jq

How do you consume a REST api in the terminal? All you can do is just curl a REST end point which will spit some uglified JSON into the console, right?

Let's say you want to get a list of public bike stations in Barcelona:

curl -s https://bicing.barcelona/get-stations


Hmmm, that's not very readable... Can you do better than this?

Yes, you can!

Meet jq — a full-fledged console JSON parser, as powerful as awk but for JSON.

Available in standard linux repos:

sudo apt install jq

as well as in homebrew on Mac:

brew install jq

Now we can display the list of all bike stations in Barcelona with JSON pretty-printing and colorization:


curl -s https://bicing.barcelona/get-stations | jq

Now, this is more readable. But how about filtering for specific bike stations, say the ones closest to our daily destinations?

I pulled the station IDs at offical Barcelona bicing
site
and pass the IDs as parameters to jq:

curl -s https://bicing.barcelona/get-stations --compressed | jq -r '.stations[] | select(.id==("153", "154", "339", "382","150"))'

Since I don't want all the fields in the JSON, I pass in the JSON keys for the data I want to keep.

Get bikes by station IDs, and extract station address, number of bikes, and number of parking slots:


curl -s https://bicing.barcelona/get-stations --compressed | jq -r '.stations[] | select(.id==("153", "154", "339", "382","150")) | .streetName,.bikes,.slots'

This looks a bit better, but by default jq prints one key per line, and I want to add some formatting, so that data for one station shows in a single line. I can use jq string interpolation to achieve this. Here is the final version which prints our data in a tabular format (available bikes, parking slots, station id, and address):

echo 'AVL\tEMP\tSID\tADR'
curl -s https://www.bicing.barcelona/get-stations --compressed | jq -r '.stations[] | select(.id==("153", "154", "339", "382","150")) | "\(.bikes)\t\(.slots)\t\(.id)\t\(.streetName), \(.streetNumber)"'

And here is the final result:

And, finally, what's the point of adding the --compressed parameter to curl?

Setting --compressed is equivalent to setting --header 'Accept-Encoding: gzip,deflate' — as a result the server will compress the output with gzip or deflate (if capable of compression), and curl will decompress the response on the fly, which will make the result appear faster since less bytes will be sent over the network.