Security Cameras
Contents
Rules For Use
Security cameras are governed by i3Detroit's Security Camera policy.
History
Use of security cameras was approved originally in 2012. The policy was updated in 2015 to be included in the standing rules. The discussion of the 2015 proposal happened largely in Slack, in the archived channel #camera-policy. The standing rules section was modified in early 2016 to replace the 4-person viewing team with the Board of Directors. It was again modified in early 2017 to expand the viewing team to consist of at least one director and either one other director, officer, or zone coordinator.
Camera Locations
List of Current Cameras
Location | View (not live) |
---|---|
Classroom | ![]() |
Craft Room | ![]() |
CNC Shop | ![]() |
Commons Area | ![]() |
Commons Area & Snack Zone | ![]() |
Electronics Lab | ![]() |
Fab Lab | ![]() |
Front & Garage Doors | |
Front Door Exterior | ![]() |
Laser Zone | ![]() |
Machine Shop 1 | |
Machine Shop 2 | ![]() |
Media Lab | ![]() |
Suite B Rear Doors | ![]() |
Suite B Rear Doors | ![]() |
Suite B Shop Doors | ![]() |
Tool Crib | ![]() |
Welding Zone | |
West Shop (Bike, Vinyl, & Jewelry zones | ![]() |
Wood Shop North | ![]() |
Wood Shop South & Tool Crib benches | ![]() |
History of Camera Location Approval
- Original list from Board_Meeting_Minutes_20121113
- Slightly modified list voted on via email vote: Membership Proposal 20121115 Allow Placement of Cameras
- List updated in early 2016
- List updated again: Meeting Minutes 20160517#Request for additional security camera
- Another update: Meeting Minutes 20170307#Camera Placements
- Note: The outside camera was in the original list, but not in the actual voted-on list. However, at the time of the 2016 update, the outside camera was presumed to be approved. It's not super relevant since we don't have an outdoor camera set up (as of 2-17-16), but it should probably be officially re-approved.
- Approved Media Lab camera at Minutes:Meeting Minutes 20180605
- Approved three cameras in Suite B at [[1]]
Status Report from Project Lead
POC:Mike
- 18 of 18 approved planned cameras have been installed. See below for technical details.
- (10/19/17) System is in working order and has been for several months now.
- (6/9/2018) System still working well, no major issues. 15/18 cameras have 240+ days continuous uptime.
To Do
Task Name | Description | Priority | Champion |
---|---|---|---|
Reconfigure Network for camera VLAN | Move all the cameras over to their own VLAN separate from the main public network. | 3 | |
Install GPU in VMServer | Install an Nvidia 6xx or newer GPU to enable GPU accelerated h.264 encoding for archived video. | 5 |
Technical Details of Zoneminder & IP Camera System
- Zoneminder v1.32.1 running on VMServer
- 1x 4TB Western Digital Purple Surveilance, 1x 4TB Western Digital Red hard disks installed in the VMServer
- 18 wired PoE IP cameras
- 13xHikvision 4.1MP DS-2CD2042WD-I 4mm IP Camera
- 5xHikvision DS-2CD2432F-IW 3MP Indoor 2.8mm IP Camera
Maintenance Scripts
Archving
Zoneminder currently stores all data as jpeg frames. This is nice and simple, but uses 10 times as much disk space as the data does once encoded as h264 video. This script is set to run daily to archive the previous day as h264 video, one file per 1-hour period per camera. It also makes sure back-ups were done on previous days.
#!/bin/bash # get a list of active monitor numbers from zoneminder # I have no idea how this works but it does camrange=$(zmu -U USERNAME -P PASSWORD -l | awk '{ print $1}' | sed 's/Id//g' | sed ':a;N;$!ba;s/\n/ /g') # associate plaintext names with monitor numbers for humans for i in {0..25}; do cameralist[$i]="undefined"; done cameralist[1]="Entry" cameralist[4]="E-Lab" cameralist[7]="FabLab" cameralist[8]="Classroom" cameralist[9]="Craftroom" cameralist[10]="MediaLab" cameralist[11]="Welding" cameralist[12]="Lasers" cameralist[13]="MachineShopWest" cameralist[14]="ToolCrib" cameralist[15]="WoodShopNorth" cameralist[16]="CommonsSW" cameralist[17]="WestShop" cameralist[18]="WoodShopSouth" cameralist[19]="ExteriorSE" cameralist[20]="CNCZone" cameralist[21]="CommonsNE" cameralist[22]="MachineShopEast" # get an offset date value - offset must be >= 1 unless libtimetravel is installed offset=$1 year=$(date +"%Y" -d "-${offset}days") month=$(date +"%m" -d "-${offset}days") day=$(date +"%d" -d "-${offset}days") shortyear=$(date +"%y" -d "-${offset}days") nextdayoffset="$((offset-1))" nextday=$(date +"%d" -d "-${nextdayoffset}days") nextdaymonth=$(date +"%m" -d "-${nextdayoffset}days") nextdayyear=$(date +"%Y" -d "-${nextdayoffset}days") ArchiveDir="/Archive/directory" # directories to put stuff mkdir $ArchiveDir/$year mkdir $ArchiveDir/$year/$month mkdir $ArchiveDir/$year/$month/$day #backup the SQL database info for the day's events mysql --user=redacted --password=zmpass --database=zm -e "select * from Events where StartTime >= '$year-$month-$day 00:00:00' AND StartTime < '$nextdayyear-$nextdaymonth-$nextday 00:00:00';" > /mnt/cameraArchive/metadata/$year-$month-$day-metadata.txt # Doing the backups for cam in $camrange do mkdir $ArchiveDir/$year/$month/$day/$cam-${cameralist[$cam]} mkdir $ArchiveDir/$year/$month/$day/$cam-${cameralist[$cam]}/logs for hour in {00..23} do # Write a list of frames to a file, in a format to pass to ffmpeg for f in /var/cache/zoneminder/events/$cam/$shortyear/$month/$day/$hour/*/*/*.jpg do echo "file '$f'" >> $ArchiveDir/$year/$month/$day/$cam-${cameralist[$cam]}/logs/${hour}00.log done # Do the actual conversion - throw every jpeg in the whole hour (in the range X0000-X9999) into one video file # Currently have no fancy output parameters. -r 25 sets 25fps, which is totally arbitrary and doesn't really matter. # Most monitors are recording at 4 or 6 fps, so this just speeds up playback/review. # ffmpeg was self-compiled due to Debian's ffmpeg/libav debacle echo "Processing $year $month $day - Hour $hour - $cam ${cameralist[$cam]}" /home/mkfink/bin/ffmpeg -f concat -safe 0 -i $ArchiveDir/$year/$month/$day/$cam-${cameralist[$cam]}/logs/${hour}00.log -vf scale=iw*.75:ih*.75 -pix_fmt yuv420p -map 0 $ArchiveDir/$year/$month/$day/${cam}-${cameralist[$cam]}/${cam}-${cameralist[$cam]}-$year$month${day}_${hour}00.mkv done done chown -R www-data:www-data $ArchiveDir/$year/$month/$day # Create a log file indicating the backup has finished # and throw a note into slack that the job finished pretext="Archiving complete for " dash="-" posttext=". Size of day encoded is " daysize=$(du -hs $ArchiveDir/$year/$month/$day | awk '{print $1}') zmsize=$(du -hsc /var/cache/zoneminder/events/*/$shortyear/$month/$day/ | tail -n1) postposttext=" from ZM data size " text=$pretext$year$dash$month$dash$day$posttext$daysize$postposttext$zmsize # make the log file echo $text > $ArchiveDir/$year/$month/$day/backupcomplete.log webhook_url="redacted" channel=#statusbots # send to slack escapedText=$(echo $text | sed 's/"/\"/g' | sed "s/'/\'/g" ) json="{\"channel\": \"$channel\", \"text\": \"$escapedText\"}" curl -s -d "payload=$json" "$webhook_url" # check for missing backups testoffset=$offset missingdays=0 dayexists=0 while [ $dayexists == 0 ] do ((testoffset++)) testyear=$(date +"%Y" -d "-${testoffset}days") testmonth=$(date +"%m" -d "-${testoffset}days") testday=$(date +"%d" -d "-${testoffset}days") if [ -f "$ArchiveDir/$testyear/$testmonth/$testday/backupcomplete.log" ] then dayexists=1 echo "true" else ((missingdays++)) echo $dayexists$testyear$testmonth$testday fi done # complain about missing backups if [ $missingdays -gt 0 ] then pretext2="Alert! Backups are incomplete or missing for " posttext2=" days. Please manually start backups." text2=$pretext2$missingdays$posttext2 escapedText=$(echo $text2 | sed 's/"/\"/g' | sed "s/'/\'/g" ) json="{\"channel\": \"$channel\", \"text\": \"$escapedText\"}" curl -s -d "payload=$json" "$webhook_url" fi |
Maintenance
Sometimes cameras get disconnected and zoneminder shows a blank blue 'no signal' image. This runs a few times a day, checking to see if any cameras are doing this.
#!/bin/bash # get a list of active monitor numbers from zoneminder range=$(zmu -U USERNAME -P PASSWORD -l | awk '{ print $1}' | sed 's/Id//g' | sed ':a;N;$!ba;s/\n/ /g') for x in $range do #echo $x zmu -U USERNAME -P PASSWORD -i -m $x redval=$(convert Monitor$x.jpg -scale 1x1\! -format '%[fx:int(255*r+.5)]' info:-) greenval=$(convert Monitor$x.jpg -scale 1x1\! -format '%[fx:int(255*g+.5)]' info:-) blueval=$(convert Monitor$x.jpg -scale 1x1\! -format '%[fx:int(255*b+.5)]' info:-) if [ $blueval -gt 190 ] && [ $redval -lt 4 ] && [ $greenval -lt 4 ] then #Run a script to complain about this in Slack /home/mkfink/camscripts/camwarn.sh $x fi rm Monitor$x.jpg done |
Zoneminder does a good job policing its own disk use (as long as it's not generating data faster than it deletes it, which happened once. Oops.) but just to make sure, and to keep track of the h264 archiviving, this reports disk use to slack every day.
#!/bin/bash #get current disk usage percent as a number CURRENT=$(df /mnt/cameraCurrent | grep / | awk '{ print $5}' | sed 's/%//g') ARCHIVE=$(df /mnt/cameraArchive | grep / | awk '{ print $5}' | sed 's/%//g') webhook_url="redacted" channel=#statusbots now=$(date) pretext="Active camera disk usage is " posttext="%. " pretext2="Archive disk usage is " text=$pretext$CURRENT$posttext$pretext2$ARCHIVE$posttext escapedText=$(echo $text | sed 's/"/\"/g' | sed "s/'/\'/g" ) json="{\"channel\": \"$channel\", \"text\": \"$escapedText\"}" curl -s -d "payload=$json" "$webhook_url" |