
Yard Lights
There are many options to set up yard lights, and I have tried most of them over the years. For example there are new solar panel charged yard lights you stick into the yard and pretty much forget about them, but they don’t work too well in the winter. There are some with timers where you set on / off times but sun rises and sets at different times throughout the year. Some yard lights have light sensors but how do you turn them off at a designated time.
I use an old computer in the garage running linux primarily as a firewall, but the same computer controls also the yard lights as well as the sprinklers and helps me brew beer. X10 modules are not too reliable but for yard lights and sprinklers they work fine. However, something as critical as beer we have to use the parallel port. We talk about the first two functions in this post. Brewing beer will have to go to another post.
X10 Firecracker modules allow one to turn power on or off from the computer’s RS232 serial COM port wirelessly and also from the remote unit. Each module is encoded with a single letter / number combination. The letters go from A to P, the numbers go from 1 to 16.
The C program to control X10 modules is called flipit. You can get it from http://code.google.com/p/flipit/. There are no dependencies that I know of, it should compile without any problems.
Let us assume that the transmitter is connected to the first COM port and also
assume that the lights are connected to the module M 1, you could turn the lights on/off with the following commands:
/usr/local/bin/flipit -t /dev/ttyS0 flip m1 on /usr/local/bin/flipit -t /dev/ttyS0 flip m1 off
The next step is to figure out what time the sun will set. This is not a trivial problem since the geography of the location and the structures around it will possibly change the sun set time. I used a lookup table for my location, you would have to find a similar table for your location. The URL to get your lookup table is at the top of the perl script below. The perl script shown at the end of this post takes one argument -d and the number of minutes delay and calculate the sun set plus delay time in military time HH:MM and print the result on stdout.
Turning off the lights at a certain time from the cron job is fairly easy. Here is a sample cron entry to turn the lights off at 11:30pm daily.
30 23 * * * /usr/local/bin/flipit -t /dev/ttyS0 flip p10 off
But, how do we turn the lights on for example 20 minutes after sun set daily. at command allows us to schedule a program to run at a later time. The best part of this program is that even if the operating system is rebooted, it will remember to run our scheduled program at the designated time after reboot.
Here is a cron entry to run at 1:15am daily to schedule our program to run 20 minutes after sun set. Make sure that cron daemon crond is running by typing service crond status. If not running, start it by typing service crond start.
15 01 * * * echo /usr/local/bin/flipit -t /dev/ttyS0 flip p10 on | at `/usr/local/bin/sunset -d 20`
This setup has been working great so far. The lights come on in the dusk and the python script shown at the end of this post accounts for the Daylight Savings Time.
Sprinklers
The best time to water the lawn is early in the morning. I have two separate circuits, one in the front which takes a lot of sun, and the one in the back with hardly any sun. We make two entries into cron file, one for each circuit. The python script sprinkler takes two arguments: the first is for the X10 module ID, the second for the number of minutes to water.
# front yard sprinkler, module n3, 25 minutes to water
05 05 * * * /usr/local/bin/sprinkler.py p9 25
# backyard sprinkler, module n4, 30 minutes to water
10 06 * * * /usr/local/bin/sprinkler.py p10 30
But since we have a computer with access to the internet, we can make the process of watering the lawn lot more efficient. Our script requests today’s weather report. If the high temperature of the day is greater than 80F, it will water daily. If less then that, it will water every other day. But only if rain or precipitation is not expected. The weather report is logged into the /tmp/sprinkler.log file. Don’t forget to update your latitude and longitude at the top of the script. You can also tweak the script to fit your needs and parse the weather report with new and unexpected terminology.
#!/usr/bin/python import re,string, sys, os, time import urllib from HTMLParser import HTMLParser #urlq = "http://www.wrh.noaa.gov/total_forecast/text_only.php?wfo=pqr&zone=orz006&county=orc067" urlq = "http://forecast.weather.gov/MapClick.php?lat=45.52&lon=-122.68&TextType=1" #print urlq f = urllib.urlopen(urlq) lines = f.readlines() f.close() #print lines for line in lines: if (string.find(line, "ay:") > -1): start = string.find(line, "ay:") + 11 line2 = line[start:] end = string.find(line2, ".<br>") forecast = line2[:end] break elif (string.find(line, "Tonight") > -1): start = string.find(line, "Tonight") + 12 line2 = line[start:] end = string.find(line2, ".<br>") forecast = line2[:end] (YY, MM, DD, j, j, j, j, j, j) = time.localtime() rain = 0 if (re.match('.*showers.*', forecast, re.IGNORECASE) or re.match('.*thunderstorms.*', forecast, re.IGNORECASE) or re.match('.*precipitation.*', forecast, re.IGNORECASE) or re.match('.*rain.*', forecast, re.IGNORECASE)): rain = 1 temp = 0 result = re.match('.*Highs around (\d+).*', forecast, re.IGNORECASE) if (result): temp = int(result.group(1)) result = re.match('.*high near (\d+).*', forecast, re.IGNORECASE) if (result): temp = int(result.group(1)) result = re.match('.*Highs (\d+) to (\d+).*', forecast, re.IGNORECASE) if (result): temp = int(result.group(2)) # even or odd day even = ((DD % 2) == 0) # water only May through September summertime = (MM >= 5) and (MM <= 9) f = open("/tmp/sprinkler.log", "a") for line in lines: f.write(line) f.write("-----\n") f.write("%d/%02d/%02d TEMP:%d RAIN:%d \n" % (YY, MM, DD, temp, rain)) f.write(forecast) f.write("\n") f.write("------------------------------------------------\n") f.write("\n\n") f.close() if (sys.argv[1] == 'n3'): # NOT raining AND (even days OR temp>80) AND (month < SEP) if (not rain and (even or (temp > 80)) and summertime): os.system("/usr/local/bin/flipit -t /dev/ttyS0 flip p11 on") time.sleep(60 * int(sys.argv[2])) os.system("/usr/local/bin/flipit -t /dev/ttyS0 flip p11 off") if (sys.argv[1] == 'n4'): if (not rain and summertime): os.system("/usr/local/bin/flipit -t /dev/ttyS0 flip p12 on") time.sleep(60 * int(sys.argv[2])) os.system("/usr/local/bin/flipit -t /dev/ttyS0 flip p12 off")
sunset perl script to figure out the sun set plus the delay time in minutes.
#!/usr/bin/perl # I got the sunrise/sunset table from the government website # shown below for Portland, Oregon. Replace the table for your # own location. $suntxt = qq( http://www.wrh.noaa.gov/pqr/sunmoon/pdxsun2006.txt o , o , PORTLAND, OREGON Astronomical Applications Dept. Location: W122 39, N45 31 Rise and Set for the Sun for 2006 U. S. Naval Observatory Washington, DC 20392-5420 Pacific Standard Time Jan. Feb. Mar. Apr. May June July Aug. Sept. Oct. Nov. Dec. Day Rise Set Rise Set Rise Set Rise Set Rise Set Rise Set Rise Set Rise Set Rise Set Rise Set Rise Set Rise Set h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m h m 01 0751 1638 0732 1717 0649 1758 0551 1839 0459 1918 0425 1952 0426 2003 0455 1939 0532 1848 0609 1751 0650 1658 0730 1629 02 0751 1639 0730 1719 0647 1759 0549 1840 0457 1919 0425 1953 0426 2003 0456 1937 0533 1846 0610 1749 0652 1656 0732 1628 03 0751 1640 0729 1720 0645 1800 0547 1841 0456 1920 0424 1954 0427 2003 0457 1936 0534 1844 0611 1747 0653 1655 0733 1628 ------ deleted other lines ------ 29 0735 1713 0651 1756 0556 1835 0502 1915 0427 1950 0425 2003 0451 1942 0528 1854 0606 1755 0646 1702 0728 1630 0751 1635 30 0734 1714 0555 1836 0500 1916 0426 1950 0425 2003 0452 1941 0530 1852 0608 1753 0647 1700 0729 1629 0751 1636 31 0733 1716 0553 1838 0426 1951 0453 1940 0531 1850 0649 1659 0751 1637 ); sub getSunSet() { $month = @_[0]; $day = @_[1]; $sunsetstr = substr($lines[10 + $day], ($month-1)*11 + 9, 4); $hr = substr($sunsetstr, 0, 2); $mn = substr($sunsetstr, 2, 2); return ($hr, $mn); } $delay = 0; if (@ARGV != 0) { if ($ARGV[0] ne "-d") { die "usage: sunset option\n" . "option: -d minutes (where minutes < 60)\n"; } else { $delay = $ARGV[1]; if ($delay >= 60) { die "delay must be less then 60 minutes."; } } } @lines = split(/\n/, $suntxt); ($d, $m, $y, $dst) = (localtime)[3, 4, 5, 8]; $y = $y + 1900; $m = $m + 1; ($hr, $mn) = &getSunSet($m, $d); if ($dst) { $hr = $hr + 1; } $mn = $mn + $delay; if ($mn > 59) { $mn = $mn - 60; $hr = $hr + 1; } printf "%02s:%02s\n", $hr, $mn;
