Hack My House: ZoneMinder’s Keeping An Eye On The Place

Hacks are often born out of unfortunate circumstances. My unfortunate circumstance was a robbery– the back door of the remodel was kicked in, and a generator was carted off. Once the police report was filed and the door screwed shut, it was time to order cameras. Oh, and record the models and serial numbers of all my tools.

We’re going to use Power over Ethernet (POE) network cameras and a ZoneMinder install. ZoneMinder has a network trigger capability, and we’ll wire some magnetic switches to our network of PXE booting Pis, using those to inform the Zoneminder server of door opening events. Beyond that, many newer cameras support the Open Network Video Interface Forum (ONVIF) protocol and can do onboard motion detection. We’ll use the same script, running on the Pi, to forward those events as well.

Many of you have pointed out that Zoneminder isn’t the only option for open source camera management. MotionEyeOS, Pikrellcam, and Shinobi are all valid options.  I’m most familiar with Zoneminder, even interviewing them on FLOSS Weekly, so that’s what I’m using.  Perhaps at some point we can revisit this decision, and compare the existing video surveillance systems.

Cameras and Installation

Zoneminder generally works with any camera that follows the modern standards.  I’m using a handful of four-megapixel Trendnet  TV-IP314PI cameras, which seem to be the sweet spot for cost and quality. These cameras have a particularly odd quirk that took me several days to understand. To get motion detection running on the camera itself, I needed to update the camera firmware, but the browser interface simply refused to select a firmware file to upload. Many IP cameras make use of a browser plugin to view the live stream, and older firmware on the Trendnet units also require that plugin in order to upload the firmware update. I finally turned to a Windows 7 VM, installed the browser plugin, and got the firmware updated.

Camera placement takes planning to be effective. Coverage of the front and back doors is a must, and seeing whether your garage door is open is quite useful as well. I also decided to cover all the windows. If you can manage, it’s useful to stretch the Ethernet cable around the outside of the house, and hold the camera in place while you or a buddy pulls it up on a cell phone, in order to find the best placement. All told, I hung nine cameras.

Ethernet in Living Color

Ethernet, lots of Ethernet.

If you’re keeping track, that’s a grand total of many Ethernet cables. To help when sorting them out, I bought several colors of cabling: red, green, yellow, blue, and white. I highly recommend a color code. Mine goes like this: PoE cameras use yellow Ethernet, Raspberry Pis use red. Each room of the house gets two more cables, white for a VoIP phone and blue for a generic connection to the network. The green cable is for running something other than Ethernet over Cat 5, like a door or temperature sensor.

When I finish the interior, I’ll terminate these Ethernet cables on a set of patch panels, using color matching keystone jacks. The colors are more than just a novelty– when you’ve run a bunch of cables, it’s far too easy to lose track of which one is which.

Some ports need PoE and some don’t. I’m also planning to use VLANs to separate the various networks. Once it’s all done, we can take a deeper dive into selecting and configuring the smart switches that run the house. Keeping the Ethernet cabling as neat as possible will make the eventual configuration task much more manageable.

Zoneminder

Zoneminder has some great documentation on how to get an install up and running, so rather than cover that ground again, we’ll look closer at how to use the Raspberry Pi network to link door sensors and ONVIF motion detection into the loop. To this end, I’ve put together the zmhelper service, and made the code available on GitHub. Well look at the more interesting snippets below.

Zmtrigger is the Zoneminder component we’ll be talking to, and it listens on TCP port 6802. On the Zoneminder server, we’ll need to open that port in the firewall, and then enable the OPT_TRIGGERS option through the web interface. This allows our service, running on a Pi, to inform Zoneminder that something important happened, and that it should start recording. Zoneminder can do motion detection natively, but offloading that work can be helpful when trying to run multiple cameras.

Speaking GPIO

A door sensor is just a simple magnetic switch installed into the door jamb. When the door is closed, the magnet in the door pulls the switch closed, completing the circuit. The Pi’s GPIO ports are perfect to monitor this. One wire from the sensor goes to ground, the other to the GPIO pin. It’s best to put a resistor between that pin and the switch, to protect in the case of accidentally powering it. Some of the GPIO pins go high or low during the boot process, and shorting a hot GPIO directly to ground is a BAD THING(tm). Some of you might be thinking about pull-up resistors. The Raspberry Pi has software configurable pull-up and pull-down resistors that are already built in, so we don’t need the external resistors.

GPIO event detection isn’t limited to just door sensors. Motion detectors are the other interesting possibility. I hope to eventually look at this in more detail, in the context of retooling an old security system to be powered by a Pi.

Rather than poll that GPIO pin every few seconds, we set up an interrupt handler.  This allows the event reporting code to trigger more quickly, and allows us to watch for ONVIF events at the same time. The other bit of magic going on here is the debouncing logic. The GPIO library does well at filtering out the bounces when there is a legitimate trigger, like the door opening. It fails at filtering out the bounces generated by the switch closing. To overcome this, we sleep for the bouncetime and check the GPIO status again.

  GPIO.setmode(GPIO.BOARD)
  GPIO.setup(gpio_pinnum, GPIO.IN, pull_up_down=gpio_resistor)
  def handler(pin):
      time.sleep(gpio_bouncetime/1000)
      if GPIO.input(gpio_pinnum) == gpio_active_state: #Debounce check.  If we're still active, it's a real event.
          s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          s.connect((zmip, zmport))
          s.send(gpio_mid +'|on+20|' + gpio_escore + '|' + gpio_ecause + '|' + gpio_etext)
          s.close()
          print("DOOR opened!")
  GPIO.add_event_detect(gpio_pinnum, gpio_edge, handler, bouncetime=gpio_bouncetime)

ONVIF Events

If ONVIF is configured, then we connect to the camera, pull any events in the cue, and wait for new events. The code here is blocking– it stops and waits for the response from the camera. That response is intentionally delayed until a new event is ready. The code then scans through the event data looking for whether there was motion detected.

  mycam = ONVIFCamera(camIP, 80, username, password, wsdl_dir='/home/pi/.local/wsdl/')
  event_service = mycam.create_events_service()
  pullpoint = mycam.create_pullpoint_service()
  req = pullpoint.create_type('PullMessages')
  req.MessageLimit=100
  while True:
    messages = Client.dict(pullpoint.PullMessages(req))
    if 'NotificationMessage' in messages:
      try:
        messages = messages['NotificationMessage']
        for x in messages:
          message = Client.dict(Client.dict(Client.dict(Client.dict(Client.dict(x)['Message'])['Message'])['Data'])['SimpleItem'])
          if message['_Name'] == 'IsMotion' and message['_Value'] == 'true':
            if time.time() - last_trigger > 15:
              print("Triggering!")
              last_trigger = time.time()
              s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
              s.connect((zmip, zmport))
              s.send(onvif_mid +'|on+20|' + onvif_escore+ '|' + onvif_ecause + '|' + onvif_etext)
              s.close()
            break
      except:
        print('Error fetching event')

We’ve looked at our first real use of the Raspberry Pi, feeding events into a Zoneminder instance. There’s more to come, like breaking the proprietary protocol on a garage door opener, building a touchscreen thermostat, and looking at how to securely access everything remotely. As always, sound off below about what you want to see in the future. Until next time, Happy Hacking!

53 thoughts on “Hack My House: ZoneMinder’s Keeping An Eye On The Place

  1. I’ve done something similar to this but using SmartThings as the trigger rather than having to fabricate a specific solution. There are several good examples on how to do so if anyone else is using ST along with existing sensors (you can even have Ring trigger it).

  2. hanging a few feet of naked wire into a GPIO without any protection might be ok if you have a pile of PIs to swap every time a thunderstorm comes along.
    BTW, is your Cat6 shielded?

    1. +10000 on this. The long cables will act as antennas and pick up large transient voltage spikes. You need to protect against large positive and negative voltage events. Digikey has a good app note about “Protecting Inputs in Digital Electronics”. In short, it says to place 10 ohm resistor in series with the cable followed by a reversed biased shottky diode to the supply (cathode to supply, anode to signal) and a shottky diode reverse biased to ground (cathode to signal, anode to ground). This protects against transient signals above the supply (shottky to the supply will clip the input signal to the supply + 0.2v) and below ground (shottky will clip input to ground -0.2v). the article also suggests following the clipping circuit with a low pass filter that has the additional benefit of preventing transients from causing false detections. I’m not sure if that will cause issues with some of the pins powering up as outputs.

      https://www.digikey.com/en/articles/techzone/2012/apr/protecting-inputs-in-digital-electronics

  3. Just a FYI, where I’m located Red cable is reserved for fire alarms or comprehensive security systems that include fire alarms. I don’t believe that is the case in most areas, but if you aren’t doing the work just for yourself a check of the building code could save you some time and money. Or just go with a different color if the choice was arbitrary to begin with.

    1. One place where I used to work, a red ethernet cable signaled that it was a “patch cable”.
      That was after I had purchased a bunch of different colored cables. (I was using the color to designate the length of the cable -in that instance red meant 2 feet, red being “2” in the resistor code).

          1. and a strong incentive for keeping the smarts in the server is cheaper cameras, and not having to rely on proprietary IE plugins to configure them. goes both ways, I prefer it centralized but I can see the benefits of offloading the processing to the cameras. Anyone who runs zoneminder and attempts to detect motion in a few 1080p/30fps feed will quickly appreciate the processing power required.

      1. No, and it only runs on Windows. But it is cheap and frankly it knocks socks off every other solution out there including low-end hardware NVRs. Zoneminder with ONVIF cameras is a painful resource hog in my experience. I hate running a Windows box as much as the next HAD reader, but Blue Iris will run on a Windows virtual machine if your hardware supports IOMMU and you pass the integrated GPU through.

    1. I have used a mix of both Zoneminder and Blue Iris running inside a VM. They both work ‘fine’, but you are right that as of 2010 when I last messed around with diy home security Blue Iris was a good distance ahead of Zoneminder in terms of UI, but crashed about once a year so couldn’t trust it.
      I ended up just moving to somewhere where I didn’t feel the need for a security system and have been a much happier person since.

  4. ” Once the police report was filed and the door screwed shut, it was time to order cameras. ”

    And land mines, and auto-turrets. Don’t forget those. Helps with any pest-control problem as well.*

    *And now I have that scene from Over The Hedge stuck in my mind. The pests won that one.

  5. Do you have any issues with ZoneMinder? Mine started to behave strangely when I added IP cameras to my already running analog ones. I have two IP cameras (some Chinese ONVIF compliant ones). One is constantly “switching off” and ZoneMinder shows only blue. But when I login to the camera directly or via their software the picture is just fine. Also readding the camera fixes the issue on the spot.

    1. I have experimented with a few different cheap ip cameras on ZM. you may find its not ZM itself but whatever codec your using to read the camera either faulting , or not being tolerant or gracefull of a few dropped frames cause the camera itself is having a fit . I have found some cameras now which dont have this issue(they had no brand but I bought a half dozen more from the same seller lol). the two worst I had were based on the TI davinci chipset.

      If both your cameras are the same but only ones faulting it kinda rules out ZM. also if your running at a really low frame rate like less than 6fps, try doubling it. Again I found a lot of the cheaper cameras, for whatever reason, couldnt run reliably slowly.

      1. and upgrade to 1.32.x adds a lot of nice features. however it requires a lot of changes to the database and the program in general so do be prepared for a little downtime if there is niggles. (my upgrades have went fine , I use the iconnor ppa) but your luck may vary. mostly it seems to be the odd database issue or directory permission issues.

    1. Oooo, I love IP Webcam! The hard part is getting the app to autostart in the event of a power failure that lasts longer than the camera’s battery. Some phones supposedly have bootloader tweaks you can do, so the code that displays the “battery charging but phone is off” animation gets replaced with your “start the camera” action. But YMMV and I’ve never pursued this to the point of having it working.

  6. I use ZM to supervise my company office, with three cameras running on a RasPi. It can make the CPU a little toasty when all three are in modect mode, even if two are leveraging the GPU, but it runs, though I sometimes get all-black frames in alarms.
    As an added bonus, I’m uploading all events above a defined max-score threshold to a cloud VM (as well as storing them on a hard drive), where a TensorFlow neural net processes them, selects the top five with intruders on it (as opposed to false alarms with no humans), and posts them to the company Slack channel.

        1. Absolutely! Having code that pulls out the salient frames to identify an intruder, and prioritizes them for off-site storage (in case the crooks cut the cable moments into their visit), is a super great feature. I’ve been wishing for something like that!

  7. We really need to get some (at least anecdotal) evidence that video surveillance actually results in arrests and convictions. I fear that even if you present video of the thief walking off with your stuff all you’re going to get from the police is, “well, when we do catch him, this will be good evidence.”

    1. Even if it is effective you have to ask yourself whether having invariably vulnerable cameras around your house is worth it. I think I’d prefer insuring things properly. Added to that is the fact that it probably doesn’t help. London not being the safest city in the world should tell us something about CCTV effectiveness.

      1. Well, it’s exactly for that reason that cameras at our house only look out onto the public space covering approaches to the building. Nobody can enter the property unrecorded without either blacking out power or Internet first or being dropped in from the sky, but if anything subverts the camera, they only get to see public spaces. The worst that can happen is a denial of service, and someone hacking the cameras to clear the way for a burglary is going to be disappointed in their return on such a big investment.

  8. What about make some decoys that are easy to steal but end up doing something undesirable to the thief? Could be something as simple as tracking them, to jamming some non critical radio band (if owning a jammer is legal but not operating it), to waiting a few minutes before releasing a lot of stinky smoke.

    1. A smoke bomb, with a fuse-wire ignition source being held in an unpowered state by the normally-closed contacts on a relay. If the supply to the unit is cut, relay closes, internal timer circuit starts charging a capacitor to fire the smoke bomb.

  9. I have been trying to get a pi set up to run zoneminder for the past couple of months. I have tried a few different approaches, but no luck. I even tried the install link you shared, but it didn’t work for me either. Hmm. I don’t know enough about Debian to know what I’m doing wrong (or maybe it is that I don’t know how to interpret the instructions?)

  10. Have ZM running four IP cameras (Smart Home Foscam clones) without any problems. Had difficulty getting the URL correct and one needs to keep the resolution down to 640 x 480 for four cameras but all in all pretty pleased.
    Question though. I want to display these on dedicated monitor powered with a pi zero. I want it to boot directly to the montage review. I don’t want to use Google Kiosk mode. I want to also display on my raspberry PI 7 inch display in the 3 gang box. Jonathan what did you use?
    Any suggestions?
    Thanks.
    Glenn.

Leave a Reply to nsayerCancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.