Wednesday, May 29, 2013

Scheduling ZoneMinder to turn on/off at sunrise/sunset

I wanted to turn ZoneMinder on and off at sunrise and sunset, to save some CPU and network traffic. This is how I did it.


ZoneMinder Setup

The ZoneMinder wiki has instructions for creating different "run states" which can be turned on/off from the command line. Since my setup is not complicated, I simply used the default "start" and "stop" run states. It also covers scheduling these run state changes, but the cron entries are static. We need times to change dynamically for sunrise/sunset.


Sunwait

"Sunwait is a small C program for calculating sunrise and sunset, as well as civil, nautical, and astronomical twilights . . . The feature that makes this program slightly unique is that it can be set to wait specific event (such as 5 minutes before sunrise), then exit. This makes it useful for 'cron' jobs or 'scheduled tasks' when you want something to happen relative to sunrise, sunset, or some other astronomical event."

So basically, we will use Cron to schedule Sunwait to "wait" until sunrise/sunset, and then run our command/script.


Download: http://www.risacher.org/sunwait/

Extract / build / move to bin
tar -xvf sunwait-20041208.tar.gz
cd sunwait-20041208
make
(ignore compilation errors, should be ok)
sudo cp sunwait /usr/bin


Cron

Add su crontab entries. Those below start at 01:00 and 13:00, plenty of time before sunrise and sunset. The important thing is that they are scheduled before the earliest sunrise/sunset.

sudo crontab -e
(select default editor)
0 01 * * * /script/location/zm-sun.sh sunrise
0 13 * * * /script/location/zm-sun.sh sunset


Main script

The script checks the current run state, changes it based on the command line argument, and does some basic logging. This example starts ZoneMinder 30 minutes before sunrise and stops it 30 minutes after sundown.

zm-sun.sh
#!/bin/bash

# script to start / stop ZoneMinder based on sunrise / sunset
#
# usage:
# zm-sun.sh sunrise
# zm-sun.sh sunset


s=`zmpkg.pl status`

if [[ "$1" == "sunrise" ]]
  then
    if [[ "$s" == "stopped" ]]
      then
        sunwait sun up -00:30:00 38.794433N, 77.069450W ; zmpkg.pl start
        s=`zmpkg.pl status`
      else
        s="already running"
    fi
elif [[ "$1" == "sunset" ]]
  then
    if [[ "$s" == "running" ]]
      then
        sunwait sun down +00:30:00 38.794433N, 77.069450W ; zmpkg.pl stop
        s=`zmpkg.pl status`
      else
        s="already stopped"
    fi
fi

echo [`date`] Zoneminder $s, $1 >> /script/location/sunwait.log

exit



Make sure to enable execute permissions on the script
chmod +x zm-sun.sh

Enjoy

7 comments:

  1. Very nice! Thank you! Do you have any more tweaks for ZoneMinder? Yours, 3esmit.

    ReplyDelete
    Replies
    1. Glad it was helpful, that's all I have for now :)

      Delete
  2. How would I switch from a Daytime Run Sate to a NightTime runstate?

    ReplyDelete
    Replies
    1. All you should need to do is edit the main script and swap a things around in the code. Give this a try:

      #!/bin/bash

      # script to start / stop ZoneMinder based on sunrise / sunset
      #
      # usage:
      # zm-sun.sh sunrise
      # zm-sun.sh sunset


      s=`zmpkg.pl status`

      if [[ "$1" == "sunrise" ]]
      then
      if [[ "$s" == "running" ]]
      then
      sunwait sun up -00:30:00 38.794433N, 77.069450W ; zmpkg.pl stop
      s=`zmpkg.pl status`
      else
      s="already stopped"
      fi
      elif [[ "$1" == "sunset" ]]
      then
      if [[ "$s" == "stopped" ]]
      then
      sunwait sun down +00:30:00 38.794433N, 77.069450W ; zmpkg.pl start
      s=`zmpkg.pl status`
      else
      s="already running"
      fi
      fi

      echo [`date`] Zoneminder $s, $1 >> /script/location/sunwait.log

      exit

      Delete
  3. Thanks. This is what I currently have running:
    #!/bin/bash

    # script to start / stop ZoneMinder based on sunrise / sunset
    #
    # usage:
    # zm-sun.sh sunrise
    # zm-sun.sh sunset


    s=`zmpkg.pl status`

    if [[ "$1" == "sunrise" ]]
    then
    # if [[ "$s" == "stopped" ]]
    # then
    sunwait sun up -00:30:00 30.115223N, 97.483887W ; zmpkg.pl Daytime
    s=`zmpkg.pl status`
    # else
    # s="already running"
    # fi
    elif [[ "$1" == "sunset" ]]
    then
    # if [[ "$s" == "running" ]]
    # then
    sunwait sun down +00:30:00 30.115223N, 97.483887W ; zmpkg.pl NightTime
    s=`zmpkg.pl status`
    # else
    # s="already stopped"
    # fi
    fi

    echo [`date`] Zoneminder $s, $1 >> /var/log/zm/zm-sunwait.log

    exit


    It seems to be working. What are you checking for stop status in your script?

    ReplyDelete
    Replies
    1. After a server reboot, zoneminder starts by default. So, sometimes it might be running when it's supposed to be off, or visa versa. So, the next time the script runs, it would unnecessarily be turning zoneminder to the state it's already in. The only time it might actually matter is when it was already running, and the service is re-started, but even then I did not test it to see if there is any interruption...

      Delete
  4. Thanks for this. Had to change the sunwait command as it has changed over the years. Note the space between Lat/Lon and offset is now "towards noon", so "-00:30:00" is both before sunrise, and after sunset.

    sunwait wait rise offset -00:30:00 38.794433N 77.069450W;

    sunwait wait set offset -00:30:00 38.794433N 77.069450W;

    Thanks again,

    ReplyDelete