mh - MisterHouse Home Automation Program
mh is a perl program for time, event, web, socket, and voice based home control functions. It currently runs on Windows 95/98/NT and on Unix platforms, including Linux.
perl objects are used for various types of objects to give a powerful but concise programming interface. Here is some example code:
$dishwasher = new X10_Item 'B1'; # B1 is the dishwasher X10 code set $dishwasher ON if time_now '10:30 PM';
speak "I am now closing the curtains at $Time_Now" if time_now "$Time_Sunset + 0:15"; speak "The time is $Time_Now" if time_cron '30,35,40,45,50 6 * * 1-5';
play(file => 'stairs_creek*.wav') if state_now $movement_sensor eq ON;
$v_bedroom_curtain = new Voice_Cmd '[open,close] the bedroom curtains'; curtain_on('bedroom', $state) if $state = said $v_bedroom_curtain;
mh [options] [files]
options are controlled in the mh\bin\mh.ini file. You can override the default mh.ini parameters in a 'private' parm file and point to that with a mh_parms environmental variable.
files is a list of members in the -code_dir that you want to run. The default is to run all .pl files.
These give the help text:
mh -h mh -help
This is the normal usage:
mh
This will only run the 3 specified members:
mh items.pl test1.pl test2.pl
This will disable the tk interface and change the code dir to the Bruce directory:
mh -tk 0 -code_dir c:\mh\code\Bruce
This will log all 'print' statements to /mh/test1.log and turn on all serial related debug messages:
mh -log_file test1.log -debug serial
The system requirements and installation instructions are detailed in
mh/docs/install.htmlThere is also a FAQ that covers misc topics in
mh/docs/faq.html
Items and events are defined by using your favorite text editor and creating members of perl code in a directory of your choice. For example, all the code that runs my house is in the /mh/code/bruce directory.
After starting mh, in addition to the timed, serial, or X10 based events that you defined, you can trigger mh actions with any of the following:
- Voice commands. On Windows platforms, you can use the MSVOICE icon in your tray (the green V) to control the Voice Recognition (VR) mode (manual, keyword-activated, or continuous). You also select your TTS (Text To Speech) voice with MSVOICE.
On Unix platforms you can use the Festival TTS engine and pick among different accents and languages. On Linux, you can install IBM's ViaVoice SDK to enable Voice Recognition.
- A web browser pointed to http://localhost:8080 . In addition to several example web interfaces, if you are familiar with HTML, you can write your own customized interface.
- Typing any voice command in a telnet window. This is customizable with the telnet.pl module.
- Typing any voice command in the command box in the mh tk gui window.
- Typing any voice command from a dos box, using the mh/bin/house command
- Having any program write voice commands to the 'xcmd_file' (see mh.ini)
- Triggering on user defined Socket or Serial port events.
Here are the basic steps of the mh program:
1: Read config files and initialize things Setup the OLE Text To Speech (TTS) object or Festival socket port Setup the OLE Voice Recognition object or socket to the viavoice_server on linux Setup the serial objects Setup the tcp socket ports
2: Read and evaluate/compile the user event code into the mh_temp.user_code file:
Optional *.mht files are processed by mh/lib/read_table_*.pl, creating *.mhp files Objects are evaluated and created. Everything else is put into a &user_loop function (at the end of mh_temp.user_code file).
3: Loop until exit, each pass executing:
Set global time/date variables Check for voice commands Read/write socket data Read/write serial data Check for timers actions Check for external command files Evaluate the &user_loop function Sleep for a while (default is 100 milliseconds), so mh does not hog the cpu.
The interpreted nature of perl allows us to change/add/delete an event member quickly, without re-starting mh. After changing code, you tell mh to re-load the user event code (step 2 above). If there is a syntax error with the new code, the previous version of the code is re-loaded.
Re-loading 20 event members with 1500 lines of event code takes about 2 seconds. With a 50 millisecond sleep time, mh takes about 2->5% of the cpu and loops about 18 times per second on our our 150 MHz PPro system.
There is lots of good perl info is at http://www.perl.com . The reference book is ``Programming Perl, 2nd edition'' by Larry Wall, Tom Christensen, & Randall Schwartz. ISBN 1-56592-149-6. It is available from http://www.oreilly.com . ``The Perl Cookbook'' is another good book that has a ton of examples in it.
For those of you using the compiled, instead of the interpreted, version of mh, the standard perl documentation can be found at http://www.perl.com/pub/v/documentation , http://language.perl.com/CPAN/doc/manual/html/index.html and/or http://www.activestate.com/ActivePerl/docs
Perl can be a pretty intimidating language to learn, due to its many options and functions, but for the purposes of using it to code mh events, there are only a few simple rules to keep in mind. Here are a few of them:
Variables start with a $
Strings are quoted with ' or ". Use " if you want variables within the string to be substituted.
Commands end with ;
Comments start with #
You can implement block comments with =begin, =cut records, as follows: print "this will run\n";
=begin print "this"; print " will not run\n"; =cut
print "all done\n";
Do loops start and end with { }
If statements can be done either of the following ways: if (test) {action} action if test;
If tests are == (or !=) for numeric data and eq (or ne) for string data.
Object methods can be specified 2 ways. Here are both examples on how use the method 'set' for an X10 object:
set $dishwasher ON; # Indirect object form $dishwasher->set(ON); # Classic 'object oriented' form
Regular expressions are used to parse strings. They are very powerful and can be quite complex, but here are a few simple rules:
They are triggered with the =~ operator By default, they are delimited with // . stands for any character (including blanks) \S stands for non blank characters (\s is for blanks) \d stands for digits * is a modifier that means 'zero or more of the previous type of character' + is a modifier that means 'one or more of the previous type of character' ? is a modifier that means 'zero or one of the previous type of character' () captures whatever is inside for use via $1, $2, $3, etc
For example:
$data = 'Mar 25, 1998 - 11:53 AM EST / 1998.03.25 1653 UTC'; $date = $1, $year = $2, $time = $3 if $data =~ /(.+), (\d\d\d\d) - (.+) EST /;
On Windows systems, path names can use the Unix convention of / instead of the DOS convention of \. You can also use \, by typically you must 'escape it' (e.g. 'c:\\mh' instead of 'c:\mh').
You can get the ActiveHome kit for $50
from at http://www.x10.com . This includes a 2 way
X10 CM11 interface a lamp module and a couple of remote controllers. They
also sell the CM17 firecracker interface which mh supports, but is a one
way interface only (does not receive X10 signals).
X10.com also sells a $50
IR transimiter that works through the
CM17 interface. Info is at http://www.x10.com/products/ux17a_bj2.htm.
This is supported via the mh IR_Item object.
Europeans can get X10 stuff from http://www.intellihome.be and http://www.hwg-telekom.de
Weeder Technologies, http://www.weedtech.com , offers the
following kits priced from $30
to $50
each. All
of these kits can share the same serial port:
2 way X10 interface 12 bit Digital IO (can be input or output, switch or button) 8 port Analog IO, 10 bit resolution Caller ID, name and number Outgoing DTMF phone monitor
The HomeBase.pm module to supports the JDS interfaces, http://www.jdstechnologies.com . See member mh/code/public/test_homebase.pl for example code.
The Homevison.pm module to supports HomeVision controler, http://www.csi3.com/homevis2.htm . This controler does 2 way X10, 2 way IR, 24 digital IO, and can output its menus to TV. See member mh/code/public/test_homevision.pl for example code.
The CPU-XA, Ocelot, and Leopard controlers from Applied Digital Inc (http://www.appdig.com/adicon.html ) are supported thru network calls to the cpuxad deamon, part of the XALIB package.
There is a $30
(assembled) pic base IR receiver you can order
from the Netherlands ($3 shipping):
http://www.evation.com/irman/
In addition to the mh/code/bruce/irman.html code, there there is free winamp, windows, and linux software for it. The schematics that it is built from are also posted on the net (elsewhere), but for $30, it hardly seems worth building it from scratch.
Here are 2 LCD displays with programable serial interfaces:
http://www.crystalfontz.com/products/634/index.html $60
http://linuxcentral.com $80 ($101 with keypad interface)
The first display is a bit newer, bigger, brighter, cheaper. But the second one has a version that include a 4x4 keypad interface (keypad available from DigiKey for $20). Both are 4 lines x 20 characters (they also have a 4x40, but no keypad interface). I ordered the 2nd one from linuxcentral, with a keypad from http://www.digikey.com (pn CKN6011-ND or GH5010-ND). A comparison of the 2, plus some nifty linux software for them, can be found at http://lcdproc.omnipotent.net
An example on how to use these displays is in mh/code/bruce/lcdproc.pl. See the header of that file for more info.
Using a these devices, and a new MP3 perl module/player, we should be able to build ourselves a mh based home MP3 Stereo! Someone has already built a perl base one at http://aelix.cs.iastate.edu/lcdat/
Here are a few other devices which look useful, but have not had code written for them:
http://www.nirvis.com/ $180 2-way IR device
http://www.cc-concepts.com/ iButton DigiLock interface as electronic keychain
The Serial_Item object can read and write to any serially connect device. There is example code in the code/public directory that shows how to interface to a ham radio TNC receiver to decode and track a GPS transmitters position.
Byterunner offers lots of types of serial cards: http://www.byterunner.com
Good sources of information on the WX200/WM918 weather stations are at http://wx200.planetfall.com/ and http://www.weatherwatchers.org/wxstation/WX-200/
. $200->$300 at Radio Shack or $200
from http://www.heartlandamerica.com
(search for weather, model WW22- 3350)
There is also now a new WMR-918, which is a wireless version, available for about $350. An mh interface is being worked on.
iButton is a family of devices that can all talk to a '1 wire' interface. Here are some urls and prices:
http://www.ibutton.com (pick shop online) - $15 DS9097U-009 or -S09 + DS1402D-DR8l for the serial interface Note: The DS9097, DS9097E, or parallel port adaptors will not work with mh - $3 DS1990A-F5 for 64 bit ID buttons. http://www.pointsix.com - They also sell the $15 interface (under iButton Products) - $10 for a 1 port relay card - $10 for a 2 port digital input or ouptut card - $6 for a 1820 temperature sensor - Info on how to wire up a 1 wire bus: http://www.pointsix.com/cgi-bin/PointSix.cgi?AppNotes&app010
If you want more reliable VR without having to use a headset, you can try these to digital array microphones:
- Labtec LVA-7280 ClearVoice Digital Microphone ($130) http://www.labtec.com/product/family.cfm?CategoryID=2
- Andrea Desktop array DA-400 ($150) http://www.andreaelectronics.com
The Labtec has a push button on top to turn it on and off and a red/green LED that blinks green when sound is heard. The Andrea has a slide switch on the side to turn it on and off. The Andrea has a bit clearer sound and is about 2/3 the size. Having a hardware switch to disable VR is handy.
Both of these mikes are ment to be used when you are at the desktop. The idea is that they filter out background noise better, so you can use them in slightly noise environments, where headsets were normally required.
The complete list of valid startup options is in the mh.ini file. Here are a few of the more important ones and their defaults:
Points to the directory that has the user code files in it.
Points to where the web interface html files are.
Use 80 if you want to use the normal web port (i.e. http://localhost ). If using something other than 80 (because you already have a web server running), add :port to url (e.g. http://localhost:8080 ).
Use 23 to enable access via 'telnet localhost', if you are running the mh/code/testtelnet.pl member. If you use a different port, you can still get to the port with 'telnet localhost port_number'.
Set this (or w=1) to enable perl -w warning messages on potential code errors. mh runs about 10% slower with this option on.
This parms allow the compile mh.exe to act as a general purpose perl interpreter, so we can run any arbitrary perl script, without having to have perl installed. See the get_weather.bat, get_tv_grid.bat, or set_clock.bat files for examples. This also allows is to use the mh run command to spawn a separate mh process to run longer running perl steps, even if we do not have perl installed. A Win32 'fork' command would be cleaner, but alas, no forks.
You can use the -debug and -log and options to turn on debug and/or log the console errata to a file. For example:
mh -debug http -log http_socket.log
will turn on socket related debug messages and log all errata to the file http_socket.log
Use time_start and time_stop to run mh in 'fast test mode', useful for debugging events. The time_increment parm sets how many simulated seconds to increment per pass. Default is 60. If only time_start is specified, normal mode will resume when the current time is reached. If time_stop is specified, mh will exit when reached. These parameters can be any time/date string, as recognized by the time_now function (see docs)
Here are some examples:
mh -time_start 0 -time_stop 24 -tk 0 -voice_text 0 mh -time_start "6 AM" -time_stop "11 PM" -time_increment 1 mh -time_start "5/14 7:10" -time_stop "5/15 10 PM" -time_increment 300
Time/Date Variables
$Time_Startup, $Time_Startup_time, $Time_Sunrise, $Time_Sunset $Time_Now, $Date_Now, $Year_Month_Now $Second, $Minute, $Hour, $Mday, $Wday, $Day, $Month, $Year, $Holiday $New_Second, $New_Minute, $New_Hour, $New_Day, $New_Month, $New_Year $Season, $Weekday, $Weekend $Time_Of_Day %Moon
The complete list of global variables can be found on the Widgets menu of the default web page or at the top of mh/bin/mh.
The %Moon
array has $Moon{phase}, $Moon{brightness},
$Moon{age}, and $Moon{new,first,full,last} entries.
The $Time_Now
and $Date_Now
variables return a
formated time and date.
The $New_* variables are true only on the pass that we enter a new Second, Minute, Hour, etc.
$Startup
is true on the first pass after starting mh.
$Reload
is true when the Reload(F1)
is requested
AND the code been reloaded because one or more code members have been
changed.
$Reread
is always true after Reload(F1),
even if
no code members have changed.
You can use File_Item to read a line of data from a file. Note: These methods currently read the entire file, so if have big files (say, >1 meg) we want to read, we should invent some new methods.
Methods: new('file_name')
name : Returns 'file_name'
read_all : Returns contents for the file. If used in a list context, a list is returned, otherwise a string of all the lines.
read_head : Returns the first few lines of a file. See file_head. read_tail : Returns the last few lines of a file. See file_tail.
read_random : Reads a random record. This also re-sets the index to the random position.
read_next : Reads the next record, according to the index. read_current : Reads the current record, according to the index.
index : Which record was last read. A couple of notes: - The index is saved between mh sessions. - If you use a File_Item that does not yet have an index set, a random index will be used and stored. set_index : Set the index.
set_watch('flag') : Sets the 'changed' time check.
changed : Returns 0 if the file was not changed since a set_watch call. When the file changes: - If 'flag' was specified in the new method, 'flag' is returned. - Otherwise, it returns the number of seconds since a set_watch call.
said : Returns data added to a file since the last call. Only one record is returned per call. This is useful for monitoring log files. See mh/code/bruce/shoutcast.pl for an example.
Examples: $f_deep_thoughts = new File_Item("$Pgm_Root/data/remarks/deep_thoughts.txt"); my $thought = read_next $f_deep_thoughts; set_index $f_deep_thoughts 1;
$f_weather_forecast = new File_Item("$Pgm_Root/data/web/weather_forecast.txt"); set_watch $f_weather_forecast; display name $f_weather_forecast if changed $f_weather_forecast;
$shoutcast_log = new File_Item 'd:/shoutcast/sc_serv.log'; print "Log data: $state" if $New_Second and $state = said $shoutcast_log;
You can use this object to store and query arbitrary data. This is more useful than 'my' variables, if you need to share data between code files, since 'my' variables are local to a code file. States of these items are also saved/restored when mh is stopped/started.
All other items that have states (e.g. X10_Item, Serial_Item, iButton, Voice_Cmd, Group) inherit all Generic_Item methods.
Methods: new state : state_now : set :
hidden(1/0) : If set to 1, the object will not show up on Tk or Web menus.
tie_items($item) : If the state of the generic_item changes, then the state of $item will be set to that same state untie_items($item) : Untie $item
tie_events($code) : It the state of the generic_item changes, then Code string $code will trigger, with the variable $state getting expanded. untie_items($code) : Untie $code
get_chaged_by : Returns which tied object caused this objects state to change.
The tie/untie methods can be done conditionally, so you can change what is tied to what on the fly. If you do this, however, you need to also make sure you also make the tie* methode call on $Reload, as the objects tie items/events are currently not stored between reloads.
Examples: $tv_grid = new Generic_Item; set $tv_grid 'channel 2 from 7:00 to 8:00 on 1/24 for This Old House'; speak "tv set to $state" if $state = state_now $tv_grid;
$wakeup_time = new Generic_Item; speak "Your wakeup time is set for $state" if $state = state_now $wakeup_time; speak "Time to wake up" if time_now state $wakeup_time;
# Since Voice_Cmd objects inherit Generic_Items methods, we can # use tie_items and tie_event like this:
$indoor_fountain = new X10_Appliance('OB'); $v_indoor_fountain = new Voice_Cmd 'Indoor fountain [on,off]'; $v_indoor_fountain -> tie_items($indoor_fountain); $v_indoor_fountain -> tie_event('speak "Ok, fountain was turned $state"');
if ($state = state_now $test_button) { my $ref = get_changed_by $test_button; print_log "Test button's was set to $state by $ref->{object_name}" if $ref; }
See mh/code/examples/generic_item.pl for more examples. See mh/code/examples/test_tie.pl for more examples on how to tie/untie items/events.
You can use this object to group and operate on groups of items:
Methods: new(@item_list) add(@item_list) set state : Returns the last state that the group was set to state_log : Returns a list array of the last max_state_log_entries (mh.ini parm) time_date stamped states.
Examples: $outside_lights = new Group($light1, $light2, $light3); $outside_lights-> add($light4, $light5); $outside_lights-> add($light6); set $outside_lights ON if time_now("$Time_Sunset + 0:15");
See mh/code/examples/test_group.pl for more examples.
This is used to query and/or control an iButton device (for more info on iButton, see the hardware section). To enable ibutton support in mh, set the mh.ini parm ibutton_port.
Methods: new $id
set $state : Sets the item to the specified state.
state : Returns the last state that was received or sent
state_now : Returns the state that was received or sent in the current pass.
state_log : Returns a list array of the last max_state_log_entries (mh.ini parm) time_date stamped states.
read_temp : Returns the temperature of temperature devices.
In addition to the above, all of the methods provided by the Hardware/iButton/Device.pm module are available (documented in mh/lib/site/Hardware/iButton/Device.pm). These methods are also part of the iButton module, but not associated with an object:
scan $family : Returns a object list of iButton devices that match $family scan_report $family : Returns a report of iButton devices that match $family
monitor : Checks the one wire bus for iButton activity
connect : Connect to the one wire bus
disconnect : Disconnect to the one wire bus
The $id required when creating new iButton_Item is the 16 hex character (64 bit) iButton id that is unique to every device. This is often printed on the larger devices. If not, you can use:
$v_iButton_list = new Voice_Cmd "List all the iButton buttons"; print_log &iButton::scan_report if said $v_iButton_list;
The last 2 characters are CRC bits and are optional. If missing (i.e. you only specify the first 14 characters), mh will calculate it. The first 2 characters are the iButton family type. Here is a list of family types:
Field Index: ------------ (1) Family code in hex (2) Number of regular memory pages (3) Length of regular memory page in bytes (4) Number of status memory pages (5) Length of status memory page in bytes (6) Max communication speed (0 regular, 1 Overdrive) (7) Memory type (see below) (8) Part number in iButton package (9) Part number in non-iButton package (10) Brief descriptions (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) ------------------------------------------------------- 01, 0, 0, 0, 0, 1, 0, DS1990A,DS2401,Unique Serial Number 02, 0, 0, 0, 0, 0, 0, DS1991,DS1205, MultiKey iButton 04, 16, 32, 0, 0, 0, 1, DS1994,DS2404,4K-bit NVRAM with Clock 05, 0, 0, 0, 0, 0, 0, DS2405,,Single Addressable Switch 06, 16, 32, 0, 0, 0, 1, DS1993,DS2403,4K-bit NVRAM 08, 4, 32, 0, 0, 0, 1, DS1992,DS2402,1K-bit NVRAM 09, 4, 32, 1, 8, 1, 2, DS1982,DS2502,1K-bit EPROM 0A, 64, 32, 0, 0, 1, 1, DS1995,DS2416,16K-bit NVRAM 0B, 64, 32, 40, 8, 1, 3, DS1985,DS2505,16K-bit EPROM 0C, 256, 32, 0, 0, 1, 1, DS1996,DS2464,64K-bit NVRAM 0F, 256, 32, 64, 8, 1, 3, DS1986,DS2506,64K-bit EPROM 10, 0, 0, 0, 0, 0, 0, DS1920,DS1820,Temperature iButton with Trips 11, 2, 32, 1, 8, 0, 2, DS1981,DS2501,512-bit EPROM 12, 4, 32, 1, 8, 0, 4, DS2407,,Dual Addressable Switch 13, 16, 32, 34, 8, 0, 3, DS1983,DS2503,4K-bit EPROM 14, 1, 32, 0, 0, 0, 5, DS1971,DS2430A,256-bit EEPROM, plus 64-bit OTP 15, 0, 0, 0, 0, 1, 0, DS87C900,,Lock Processor 16, 0, 0, 0, 0, 0, 0, DS1954,,Crypto iButton 18, 4, 32, 0, 0, 1, 6, DS1963S,4K-bit Transaction iButton with SHA 1A, 16, 32, 0, 0, 1, 6, DS1963,,4K-bit Transaction iButton 1C, 4, 32, 0, 0, 1, 6, DS2422,,1K-bit EconoRAM with Counter Input 1D, 16, 32, 0, 0, 1, 6, DS2423,,4K-bit EconoRAM with Counter Input 1F, 0, 32, 0, 0, 0, 0, DS2409,,One-Wire Net Coupler 20, 3, 8, 0, 0, 1, 9, DS2450,,Quad A-D Converter 21, 16, 32, 0, 0, 1, 8, DS1921,,Temperature Recorder iButton 23, 16, 32, 0, 0, 1, 7, DS1973,DS2433,4K-bit EEPROM 22 DS1822 temperature button 40, 16, 32, 0, 0, 0, 1, DS1608,,Battery Pack Clock
Examples:
$v_iButton_connect = new Voice_Cmd "[Connect,Disconnect] to the iButton bus"; if ($state = said $v_iButton_connect) { if ($state eq 'Connect') { print_log &iButton::connect($config_parms{iButton_port}); } else { print_log &iButton::disconnect; } }
$ib_bruce = new iButton '010000012345ef'; speak 'Hi Bruce' if ON eq state_now $ib_bruce; speak 'Later' if OFF eq state_now $ib_bruce;
$ib_relay1 = new iButton '12000000123456ff'; $v_iButton_relay1 = new Voice_Cmd "Turn relay1 [on,off]"; if ($state = said $v_iButton_relay1) { print_log "Setting iButton relay1 to $state"; set $ib_relay1 $state; }
$ib_temp1 = new iButton '1000000029a14f'; $ib_temp2 = new iButton '1000000029f5d6'; my @ib_temps = ($ib_temp1, $ib_temp2);
$v_iButton_readtemp = new Voice_Cmd "Read the iButton temperature [1,2]"; if ($state = said $v_iButton_readtemp) { my $b = $ib_temps[$state-1]; my $temp = read_temp $b; print_log "Temp for sensor $state: $temp F"; logit("$config_parms{data_dir}/iButton_temps.log", "$state: $temp"); } if ($New_Second and !($Minute % 5)) { run_voice_cmd 'Read the iButton temperature 1' if $Second == 11; run_voice_cmd 'Read the iButton temperature 2' if $Second == 22; }
This object controls IR transmiters. Currently only the X10 IR Commander (http://www.x10.com/products/ux17a_bj2.htm) is supported. The X10 IR Commander receives commands from the wireless CM17 (firecracker) interface.
Currently, you must use the X10 supplied software (http://www.x10.com/commander.htm and/or ftp://ftp.x10.com/pub/applications/commander/) to program the IR Commander to use the codes for your various remotes.
Methods: new $type, $code : Creates a new Item. Here are the valid types: TV, VCR, CAB, CD, SAT, DVD Currently, $code simply controls if 2 or 3 digits are transmited for channels: 2digit (this is the default if $code is not spedified) 3digit
state : Returns the last state that was sent state_now : Returns the state that was sent in the current pass. state_log : Returns a list array of the last max_state_log_entries (mh.ini parm) time_date stamped states.
set $command : Sends out commands to the IR device. Here is a list of valid commands: Note: Commands are case insensitive
POWER MUTE CH+ CH- VOL+ VOL- 1 2 3 4 5 6 7 8 9 0 MENU ENTER FF REW RECORD PAUSE PLAY STOP AVSWITCH DISPLAY UP DOWN LEFT RIGHT SKIPDOWN SKIPUP TITLE SUBTITLE EXIT OK RETURN
Examples:
$TV = new IR_Item 'TV'; $v_tv_control = new Voice_Cmd("tv [power,on,off,mute,vol+,vol-,ch+,ch-]"); set $TV $state if $state = said $v_tv_control;
$VCR = new IR_Item 'vcr', '3digit'; set $VCR "12,RECORD" if time_cron('59 19 * * 3'); set $VCR "STOP" if time_cron('00 20 * * 3');
You can use this object to run external programs. On Win32 systems, the Win32::Process function is used. On Unix systems, the fork function is used. On either system, the following methods work in the same way:
Methods: new('program1 arguments', 'program2 arguments', ...) set('program1 arguments', 'program2 arguments', ...) add('program3 arguments', 'program4 arguments', ...)
If you specify more than one program, they are run sequentially. done_now returns 1 after the last program is done.
If program starts with &, then 'program arguments' is eval-ed as an internal mh function. Otherwise, 'program arguments' is run as an external command.
On Windows, the &-> eval trick is supposed to work with perl 5.6+ (which has fork), but unfortunatly, it causes perl to crash often, so is probably not useful yet.
start : Starts the process.
stop : Stops the process. If not process is specified, all active Process_Items are stopped.
done : Returns the time (seconds since epoch) that the process finished. If the process has been started, but has not yet finished, it returns 0.
done_now : Is true for the pass that the process finished on.
Examples: my $slashdot_news = "$Pgm_Root/data/web/slashdot_news.txt"; $p_slashdot_news = new Process_Item("get_slashdot_news > $slashdot_news"); start $p_slashdot_news if time_now('6:30 AM'); display $slashdot_news if done_now $p_slashdot_news;
$p_report_weblog = new Process_Item; if (time_now '2 AM') { set $p_report_weblog "report_weblog /mh/data/logs/server.$Year_Month_Now.log"; start $p_report_weblog; }
# Example of multiple commands $test_process1 = new Process_Item; set $test_process1 'sleep 1', 'sleep 2'; add $test_process1 'sleep 1';
# Example of running an internal mh subroutine $v_test_ftp = new Voice_Cmd 'Test background ftp [get,put]'; $p_test_ftp = new Process_Item; if ($state = said $v_test_ftp) { set $p_test_ftp "&main::net_ftp(file => '/tmp/junk1.txt', " . "file_remote => 'incoming/junk1.txt'," . "command => '$state')"; start $p_test_ftpb; } print_log "Ftp command done" if done_now $p_test_ftp;
Serial_Item is used whenever you want read or write serial port data.
Methods: new('data_stream', 'state_name', 'serial_port') add('data_stream', 'state_name')
If 'serial_port' is not specified, then: For outgoing X10 data, the first valid port from this list is used: cm11, cm17, homevision, homebase, weeder, serial1, serial2, ... For other outgoing data, the first valid port from this list is used: weeder, serial1, serial2, ...
For incoming data, the 'serial_port' parm is not important, as data from the cm11, homebase, and weeder ports is processed in the same way.
For the generic ports (serial1, serial2, ...), incoming data is processed only by user specified 'said' methods.
state : Returns the last state that was received or sent
state_now : Returns the state that was received or sent in the current pass.
state_log : Returns a list array of the last max_state_log_entries (mh.ini parm) time_date stamped states.
set_dtr : Sets/resets the DTR line set_rcs : Sets/resets the RCS line. For example:
set_dtr $port 1; set_rcs $port 0;
set : Sets the item to the specified state. - If the specified state has not been defined with 'new' or 'add', the state data is sent. Otherwise the data_stream associated with that state is sent.
set_data : Use this to put data back into the serial read buffer. Typically used when you have processed only some of the serial data, and you want to put the rest back because it is incomplete. Here is an example:
if (my $data = said $wx200_port) { my $remainder = &read_wx200($data, \%weather); set_data $wx200_port $remainder if $remainder; }
said : Returns a data record received by the port. - Characters are spooled into one record until a '\n' (newline) is received. Only then does 'said $item' become valid, and it is reset as soon as the said method is executed.
- Note: If you want to process binary serial data, specify serial#_datatype=raw in the mh.ini file. This will cause said to return any data read immediately, rather than buffering up data until a newline is read.
start : Re-starts the serial port after a stop command or after starting with the port already in use.
stop : Stops using the serial port for this item. That allows other programs to share the port (e.g. let mh use the modem for caller ID, but turn that function off when using the modem to call out).
is_stopped : True if the port is not active is_available : True if the port is not in used by another program
set_icon : Point to the icon member you want the web interface to use. See the 'Customizing the web interface' section of this document for details.
set_info : Adds additional information. This will show up as a popup window on the web interface, when the mouse hovers over the command text.
set_order: Contols the order that the commands are listed in web Category list. The default is alphabetically by file, then by name.
will show up as a popup window on the web interface, when the mouse hovers over the command text.
Examples: $garage_movement = new Serial_Item('XI2'); speak "Someone is in the garage" if state_now $garage_movement;
$tnc_output = new Serial_Item ('CONV', 'converse', 'serial1'); $tnc_output -> add ('?WX?', 'wxquery'); set $tnc_output 'converse'; set $tnc_output "EMAIL :userid\@computers.com Test E-Mail - $CurrentTemp deg.{0"; my $serial_data = said $tnc_output;
$v_tnc_close = new Voice_Cmd("close the tnc serial port"); stop $tnc_output if said $v_tnc_close; start $tnc_output if $New_Minute and is_stopped $tnc_output and is_available $tnc_output;
See mh/code/examples/serial_port_examples.pl for more Serial_Item examples.
Same as Serial_Item, except the 'X' prefix is prepended, to indicate an X10 command, and it has pre-defined states 'on', 'off'. 'brighten', 'dim', and '+5'->'+95 and '-5'->'-95'. The +-## states increase/decrease brightness by ## percent.
You can specify a 2nd argument, if you are using more than one X10 interface and you want to control an X10_Item with a specific interface. For example, if you want to control the local module on a RF Transeiver, you can tell mh to use the RF CM17 interface, like this:
$test_light = new X10_Item('A1', 'CM17');
Note that the X10 device code is numbered 1->9,A->G. For example device 16 in house code P would be PG.
If a single character is use (e.g. X10_Item('D')),
then 'on'
and 'off' states are translated to ALL_ON and ALL_OFF commands for that
house code. For example:
$v_test_lights = new Voice_Cmd("All lights [on,off]"); $test_lights = new X10_Item("O"); set $test_lights $state if $state = said $v_test_lights;
This item also has a built in timer with the set_with_timer method. For example, this event will turn on a on a warning light to 20% for 5 seconds:
set_with_timer $watchdog_light '20%', 5 if file_unchanged $watchdog_file;
If you have one of the newer (more expensive) LM14A/PLM21 2 way X10 pro lamp modules, you can tell mh to send an extended X10 codes that let them be set directly to a specific brightness level using a Preset Dim extended code. The older modules can only be brightened/dimed.
The 64 extened X10 Preset Dim codes can used most easily by using the '#%' state. Note this differs from the +-# states described above, which increase/decrease brightness relative to the current state. The #% state goes directly to that level, independent of the existing state.
To use the #% preset dim states, you need to use a 3rd argument on X10_Item to tell it that you have a LM14 lamp module (the only module that currently supports Preset Dim). For example:
$test_light2 = new X10_Lamp('O7', 'CM11', 'LM14'); $v_test_light2 = new Voice_Cmd("Set test light to [on,off,bright,dim,5%,10%,20%,30%,40%,50%,60%,70%,80%,90%]"); set $test_light2 $state if $state = said $v_test_light2;
You can also use them directly, using &P## (## = 1->64) as shown in this example:
$test_light1 = new X10_Item('O7'); $v_test_light1 = new Voice_Cmd("Set test light to [on,off,bright,dim,&P3,&P10,&P30,&P40,&P50,&P60]"); set $test_light1 $state if $state = said $v_test_light1;
There is another set of Preset Dim commands that are used by some modules (e.g. the RCS TX15 thermostate). These 32 non-extended Preset Dim codes can be coded directly, using the following table:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 PRESET_DIM1 M N O P C D A B E F G H K L I J
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 PRESET_DIM2 M N O P C D A B E F G H K L I J
For example:
$TX10 = new Serial_Item('XM4' . 'E' . 'PRESET_DIM1', 'Increase temp'); #preset 8='E' $TX10 -> add ('XM4' . 'F' . 'PRESET_DIM1', 'Decrease temp'); #preset 9='F' $TX10 -> add ('XM4' . 'O' . 'PRESET_DIM2', 'Preset on'); #preset 18='O' $TX10 -> add ('XM4' . 'P' . 'PRESET_DIM2', 'Preset off'); #preset 19='P'
Same as X10_Item, except it has only has pre-defined states 'on' and 'off'
Craig is working on this one.
You can use the Socket_Item object to read and/or write to TCP/IP socket ports.
Server socket ports are specified with mh.ini parms (see 'server options' in mh.ini)
Methods:
new('data_stream', 'state_name', 'host_port', 'name', 'host_protocol') add('data_stream', 'state_name')
'host_port' must be of the form ip_address:port or it must match a server* parm in mh.ini (e.g. server1).
'host_protocol' can be 'tcp' or 'udp'. Default is 'tcp'
'name' is optional. If used you can set debug to this string to turn on debug for just this socket data. set_port : Allows you to change the server_name/port.
state : Returns the last state that was received or sent
state_now : Returns the state that was received or sent in the current pass.
set : Sets the item to the specified state. - If the specified state has not been defined with 'new' or 'add', the state data is sent. Otherwise the data_stream associated with that state is sent.
set_echo : Use this to control echoing of incoming characters: set_echo $my_socket 0 -> Do not echo incoming characters set_echo $my_socket 1 -> echo incoming characters set_echo $my_socket '*' -> echo incoming characters with *
said : Returns a data record received by the port. - Note: If you want to process binary socket data, specify server_*_datatype = raw in the mh.ini file. This will cause said to return any data read immediately, rather than buffering up data until a newline is read.
handle : Returns the socket handle, so you can loop on reading/writing data to it directly.
start : Connect to the specified port. This allows mh to act as a client, rather than a server, and initiate communications with a server.
stop : This will drop the client or server from mh and free up the port.
active : True if the port is active active_now : True for the mh pass that the socket first becomes active. inactive_now : True for the mh pass that the socket first becomes inactive.
Examples:
$socket_server = new Socket_Item(undef, undef, 'server_speak'); if (my $data = said $socket_server) { print "speak_server socket data: $data\n"; speak $data; }
my $lcdproc_address = 'misterhouse:13666'; $lcdproc = new Socket_Item(undef, undef, $lcdproc_address, 'lcdproc');
$Vcmd_viavoice = new main::Socket_Item(undef, undef, 'localhost:3234', 'viavoice');
More examples are in mh/code/bruce/telnet.pl, mh/code/public/Brian/tracker.pl, my/code/bruce/mh_watchdog.pl, and mh/code/test/socket_test.pl
The Timer object can be used to run an action one or more times, at a specified interval.
Methods:
new : Used to create the object.
set($period, $action, $cycles) $period is the timer period in seconds $action (optional) is the command to run when the timer expires $cycles (optional) is how many times to repeat the timer
unset : Unset the timer. 'set $my_timer 0' has the same effect.
active : Returns true if the timer is still running. inactive : Returns true if the timer is has expired or has not been set. expired : Returns true for the one pass after the timer has expired.
run_action : Runs the timers action, even if the timer has not expired.
hours_remaining, hours_remaining_now minutes_remaining, minutes_remaining_now seconds_remaining, seconds_remaining_now
These methods return the hours, minutes or seconds remaining on the timer. The _now methods only return the remaining time on the hour, minute, or second boundary.
Examples:
$timer_laundary = new Timer; $v_laundary_timer = new Voice_Cmd('Laundary timer [on,off]'); if ($state = said $v_laundary_timer) { if ($state eq ON) { play('rooms' => 'shop', 'file' => 'cloths_started.wav'); set $timer_laundary 35*60, 'speak "rooms=all The laundary clothes done"', 4; } else { speak 'rooms=shop The laundry timer has been turned off.'; unset $timer_laundary; } }
See mh/code/bruce/timer_minutes.pl and timer_seconds.pl for more examples.
Use the Voice_Cmd object to create voice commands. Even without a Voice Recognition engine installed, this is useful as these commands can also be run from the Tk, web, telnet, and file interfaces.
Methods: new($command, $response, $confirm, $vocabulary)
$command can be a simple string (e.g. 'What time is it') or it can include a list of 'states' (e.g. 'Turn the light [on,off]'). The state enumeration group is a comma delimited string surrounded with [].
In addition to one state enumeration group, you can specify any number of phrase enumeration groups. These are comma delimited strings surounded by {} (e.g. 'Turn the {family room,downstairs} TV [on,off]'). Use this when you have several different ways to describe the same thing.
$response is the text or wave file that will be played back when the VR engine detects this command. If not defined, the response is : "you said $command"
You can put %STATE% or any variable in the response string and have it substituted/evaluated when the response is spoken
$confirm is either 0 or 1 (default is 0). If set to 1, then mh will ask 'Confirm with a yes or a no'. If yes or no is not heard within 10 seconds, the command is aborted.
$vocabulary allows you to define multiple vocabularies. You can then use these functions to enable and disable the vocabularies:
&Voice_Cmd::enablevocab($vocabulary) &Voice_Cmd::disablevocab($vocabulary)
Vocabularies are enabled by default. The default vocabulary is 'misterhouse'. See mh/code/bruce/viavoice_control.pl for examples. This code allows you to switch between 'awake', 'asleep', and 'off' VR modes.
NOTE: Currently only the viavoice VR engine (mh.ini parm voice_cmd=viavoice) will use the $response, $confirm, and $vocabulary_name options. We may be able to create a viavoice_server for windows, but that would probably not be free like it is on linux. If you have a linux box on your network, you can have your windows mh use the linux viavoice_server process.
said : Is true for the one pass after the command was issued. If the command was built from a list of possible states, then said returns the state that matches.
state : Returns the same thing as said, except it is valid for all passes, not just the pass after the command was issued. set_icon : Point to the icon member you want the web interface to use. See the 'Customizing the web interface' section of this document for details.
Examples: $v_backyard_light = new Voice_Cmd 'Backyard Light [on,off]'; set $backyard_light $state if $state = said $v_backyard_light;
$v_test1 = new Voice_Cmd '{turn,set} the {living,famliy} room {light,lights} [on,off]'; $v_test2 = new Voice_Cmd '{Please, } tell me the time'; $v_test3 = new Voice_Cmd '{What time is it,Tell me the time}'; $v_fan = new Voice_Cmd 'Fan [on,off]', 'Ok, I turned the fan $v_indoor_fountain->{said}'; $v_fan = new Voice_Cmd 'Fan [on,off]', 'Ok, I turned the fan %STATE%';
See mh/code/examples/Voice_Cmd_enumeration.pl for more Voice_Cmd examples
In addition to the said command on a specific object, you can use &Voice_Cmd::said_this_pass to detect which command was spoken this pass and &Voice_Cmd::noise_this_pass to detect if noise was detected this pass (this function currently only works with viavoice).
Examples:
if (my $speak_num = &Voice_Cmd::said_this_pass) { my $text = &Voice_Cmd::text_by_num($speak_num); print_log "spoken text: $speak_num, $text"; }
if (my $text = &Voice_Cmd::noise_this_pass) { print_log "Noise detected" if $text eq 'Noise'; }
See mh/code/bruce/lcdproc.pl for more examples.
Calls up the web browser defined my the mh.ini browser parm
Examples:
browser 'http://misterhouse.net'; browser "$config_parms{tracking_dir}/today.html" if said $v_show_tracking;
Converts from degrees Kelvin/Centigrade to Farenheight
Example:
$weather{TempF} = convert_c2f($weather{TempC});
Converts 0->360 degrees into north, north east, east, etc
Example:
speak "Wind speed is " . round($weather{WindAvgSpeed}) . " from the " . convert_wind_direction($weather{WindAvgDir});
Displays the specified text string, text file contents, or bitmap (.gif or .jpg).
Usage:
display($file_or_text, $time, $title, $font);
$file_or_text can be a file or a text string. $time (optional) is how long (in seconds) till the box will auto-close. Defaults to 120. Use 0 to disable auto-close. $title (optional) is the title of the display box.
Examples:
display "Internet message: $msg", 300, 'Internet message'; display $f_trivia_answer; display '/pictures/photo.jpg';
Returns 1 if the specified file has change since last checked. Returns 0 if the file has not changed. Returns undef if we don't know (i.e. first check after mh was started).
Example:
print_log "File $file has change" if file_change($file);
Returns 0 if the specified file has change since last checked. Returns 1 if the file has not changed. Returns undef if we don't know (i.e. first check after mh was started).
Example:
my $watchdog_file = '//dm/d/misterhouse/mh/data/mh.time'; if (file_unchanged $watchdog_file) { speak "MisterHouse has stopped running on the Nick's box"; set_with_timer $watchdog_light '20%', 5; }
Returns true if the contents of the 2 specified files are different.
Example:
print_log "Files are different" if file_diff($file1, $file2);
Reads data from a file. If used in a list context, a list is returned, otherwise a string of all the lines.
Examples:
@data = file_read($file); $data = file_read($file);
Writes data into a file. Like logit, except it writes over, rather than appends to, a file.
Examples:
file_write($file, $data);
Concatonates data from one file onto another.
Examples:
file_cat $file, $file_total;
file_cat "$config_parms{html_dir}/aprs/week2.html", "$config_parms{html_dir}/aprs/old/${Year_Month_Now}.html";
Returns the first/last few lines of a file
Examples:
@data = file_head($file, $lines); $data = file_tail($file, $lines);
If $lines is not specified, the default is 3. If used in a list context, a list is returned, otherwise a string of all the requested lines is returned.
Returns the given string, with carriage returns and line feeds filtered out
Examples:
print_log filter_cr get "$url/play?p=$config_parms{mp3_program_password}";
Appends data into a log file. Use file_write to write over a file.
Usage:
logit($log_file, $log_data, $log_format, $head_tail);
$log_format=0 => Data is written as is, no /n, no time_date stamp.
$log_format=## => Every log entry is preceded with a time_date stamp. ## is passed to &time_date_stamp to determine the format. Also, /r/n is stripped so we get only one record per call.
The default $log_format is 14.
$head_tail => If 1, data is logged to the top of the file, otherwise it is added to the bottom.
Examples:
logit("$Pgm_Root/data/logs/wx200.$Year_Month_Now.log", $data, 0); logit("$Pgm_Root/data/phone/logs/callerid.$Year_Month_Now.log", "$cid_number $cid_name");
Logs data into a dbm file. If data for that key exists, it is overwritten. dbm files are useful if you want to store key-value data or hash arrays onto disk for later use.
Usage:
logit_dbm($log_file, $log_key, $log_data);
Examples:
logit_dbm("$Pgm_Root/data/phone/callerid.dbm", $cid_number, "$Time_Now $Date_Now $Year name=$cid_name");
See display_callers for an example of how to read dbm files.
Use this to play wave or system sound files. There are 2 modes of calling it:
Simple mode: play $file_name
Options mode: play(option => 'value', file => $file_name);
Options are: rooms => 'room_names' You need to code a pa_stub.pl routine to allow this to work with your PA relays. time => $time The amount of time to leave the PA speakers on for rooms mode => $mode $mode can be: wait : mh will pause (hang), until wav file finishes loop : mh will loop on the wav file stop : mh will stop playing the previous file async : mh will play wave file asynchronously (in the background). This is the default.
mode => 'unmuted' Forces speech, even in mute and offline mode
You can control the default mode parm with the mh.ini play_mode parm.
If $file_name
is a blank or comma delimited list of files,
they are played sequentially. If it is a wild-carded file specification
(e.g. ``movement*.wav''), then a file is picked at random from the files
that match.
If the file does not include a path specification, the file is looked for in the mh/sounds directory.
If the file starts with System or with sound_, the sound file is NOT logged in the speak log.
Here are the names of some of the Windows System sounds. Use the Control Panel Sounds menu to change their associated wav files:
- MenuPopup - SystemDefault - SystemAsterisk - SystemExclamation - SystemExit - SystemHand - SystemQuestion - SystemStart
Examples:
# Play a random 'garage open/close' wav file if($state = state_now $garage_door) { # $state will be open or close play(rooms => 'all', file => "garage_door_" . $state . "*.wav"); }
play(file => 'SystemAsterisk') if $Reload;
play "fun/*.wav" if time_cron '* 9 * * 6';
Use these to print to the various logs. These logs are listed in Tk and web frames.
print_log is typically used for fairly frequent, not too important messages.
print_msg is typically used for less frequent, more important messages.
print_speaklog is called by the speak and play functions, so should not be normally used in user code.
You can use the run function to run a program as a separate process. On Windows, the perl Win32::Process function is used, and on Unix, the program is forked with &.
If you specify 'inline' as the first argument, the program will not be a background process/forked. mh will pause until the program is done.
Examples: run('IR_cmd VCR,3,6,RECORD'); # Start a VCR recording run 'rasdial /disconnect'; # Log off from the net (on Windows) run 'inline', 'some_fast_command_here'; run 'mplayer.exe /play /close c:\win98\media\canyon.mid'; # Play a midi file
You can use this to have one event trigger another voice command event.
Example:
run_voice_cmd 'Get the top10 list' if time_now('6:30 AM');
Read data from a dbm file. If a key is passed, only that one record is returned. Otherwise, the whole dbm is read.
Usage:
$value = read_dbm($dbm_file, $key); %data = read_dbm($dbm_file);
Search a dbm file for matches to a string. It currently searches both key and value.
Usage:
($count_searched, $count_matched, %results) = search_dbm($dbm_file, $search_string);
See mh/code/bruce/phone.pl and mh/bin/display_callers for examples.
On Windows systems, use these functions to control other programs. Full documentation is mh/lib/site/Win32/setupsup.html.
SendKeys($window, $keystr, $activate, [$timeout]) WaitForAnyWindow($title, \$window, $timeout, [$refresh])
WaitForAnyWindow will set the window handle for the window that matches the (sub)string specified in $title.
SendKeys sends the keys. Here is an excerpt of the valid key list:
ALT+ alt down ALT- alt up CTRL+ ctrl down CTRL- ctrl up SHIFT+ shift down SHIFT- shift up TAB tabulator RET return ESC escape BACK backspace DEL delete INS insert HELP help LEFT arrow left RIGHT arrow right UP arrow up DN arrow down PGUP page up PGDN page down BEG pos1 END end F1 function 1 ... F12 function 12 NUM0 0 on the num block ... NUM9 9 on the num block NUM* multiply key on the num block NUM+ add key on the num block NUM- minus key on the num block NUM/ divide key on the num block
sendkeys_find_window is an mh subroutine that calls WaitForAnyWindow. If
the window is not found and $program
is specified, it will
start $program
and wait for the window to appear, then return
the window handle.
sendkey_find_window($title, [$program]);
Here are a few examples:
# Start winamp, if it is not running unless (&WaitForAnyWindow('Winamp', \$window, 100,100)) { print_log "Starting winamp"; run $config_parms{mp3_program}; }
# Send/receive mail, using sendkeys_find_window to start the program # if it is not already running if (my $window = &sendkeys_find_window('Outlook', 'D:\msOffice\Office\OUTLOOK.EXE')) { my $keys = '\\alt+\\tss\\alt-\\'; # For Outlook Express my $keys = '\\alt\\te\\ret\\'; # For Outlook &SendKeys($window, $keys, 1, 500); }
Passes specified text to the TTS program. There are 2 modes of calling it:
Simple mode: speak "text to speak"
Options mode: speak(option => 'value', text => "text to speak"); Options are: rooms => 'room_name' You need to code a pa_stub.pl routine to allow this to work with your PA relays. mode => 'unmuted' Forces speech, even in mute and offline mode
These options are for the Festival speech engine (but don't work yet): volume => 'loud' or 'soft' rate => '+-nn%' (e.g. +10% or -30%) voice => 'name' Examples:
$test_voice1 = new Voice_Cmd "Say something with a [loud,soft] voice"; $test_voice2 = new Voice_Cmd "Say something at a [fast,slow] speed"; $test_voice3 = new Voice_Cmd "Say something in voice [male1,female1]";
speak(volume => $state, text => "This is an example of $state text") if $state = said $test_voice1; speak(rate => $state, text => "This is an example of $state text") if $state = said $test_voice2; speak(voice => $state, text => "This is an example of a $state voice") if $state = said $test_voice3;
You can use this function to return a time/date string.
Usage:
time_date_stamp($format, $file_or_time);
$format can be any of the following:
1: Monday, 08/26/96 11:01 PM 2: Monday Jul 27 14:00 1996 (seems to be more compatable with javascript parsing) 3: Monday Jul 27 at 6 AM 4: 6:05 AM on Monday, Jul 27 5: 6:05 AM 6: Mon, Jul 27 7: Mon 01:02PM 8: 6:05 (skip the AM PM) 9: 04/14/97 10:28 PM 10: year_month, with leading 0, so log files are sorted ok (e.g. 97_01) 11: 01/31/98 (mm/dd/yy) 12: 04/14/97 14:28:00 13: 14:28:33 14: Mon 04/14/99 14:28:00 15: Sunday, December 25th 16: 04/14/97 2:28:00 PM
The second argument can be either time (in epoc seconds) or a file, where
it will use the time that it was last modified. The default is
$Time
(current time).
$lcd_data{1} = &time_date_stamp(14, $Time);
speak "File was changed today" if time_date_stamp(6, $file) eq time_date_stamp(6);
Note: The mh.ini time_format and date_format parms can be used to modify these formats to non-us standards (e.g. no AM/PM, use dd/mm instead of mm/dd).
The cron time format matches the unix cron format.
Cron Format: minutes hours day_of_month month day_of_week minutes: 0-59 hours: 0-23 dom: 1-31 (day of month) month: 1-12 dow: 0-6 (day of week 0=Sunday 6=Saturday)
You can use a comma delimited list for any of these fields. * matches all values.
Examples:
# Speak time every 15 minutes, between 7 am and 8 am on weekdays only speak $Time_Now if time_cron '0,15,30,45 7,8 * * 1-5';
# Change PA speaker mode at 9 pm, every day pa_sleep_mode('kids', 1) if time_cron '* 21 * * * ';
These return true if the specified time is greater or less than the current time. Format can have the same sorts of offsets as described in time_now below. Note unlike time_now which is only true once, these functions will return a true value for every pass that they are true.
Example: curtain_on('bedroom', OPEN) if time_cron('22 6 * * 1-5') and time_greater_than("$Time_Sunrise + 0:15");
time_now is evaluate to true for the 1 pass that matches the specified time/date. Date is optional. You can specify + or - offset, in hours:minutes:seconds. Time can include AM/PM or be in 24 hour format. The best way to see how it works is to look the following examples:
Examples:
run_voice_cmd 'close the living room curtains' if time_now $Time_Sunset; set $backyard_light ON if time_now("$Time_Sunset + 0:15"); set $left_bedroom_light +50 if $Weekday and time_now "$wakeup_time - 0:01"; speak "Remember dentist appointment" if time_now '5/27/98 8:15 AM'; run('min', 'IR_cmd VCR,4,RECORD') if time_now '2/07 18:00'; run('min', 'IR_cmd VCR,STOP') if time_now "$date $stop - 00:02";
Use time_random to code random events. It uses the same time format as time_cron, but includes frequency parameter that specifies how often the event should trigger.
Usage: time_random($cron_spec, $frequency)
Examples:
# Speak something goofy once an hour on weekends speak(read_next $april_fools) if time_random('* 8-22 * * 0,6', 60);
# Toggle a light on an off randomly every 30 minutes if (time_random('* 18-22 * * *', 30)) { $state = (ON eq state $bedroom_light) ? OFF : ON; set $bedroom_light $state; }
Use these functions to add a Tk button widget to the mh tk grid (tk_button) or the tk menu_bar (tk_mbutton). &tk_button will accept multiple variables, displaying them in a row in the grid.
Usage: &tk_mbutton('Button Name', \&subroutine); &tk_button('Button1', \&sub1); &tk_button('Button1', \&sub1, 'Button2', \&sub2,'Button3', \&sub3);
Examples:
&tk_mbutton('Help', \&help); &tk_button('Reload(F1)', \&read_code, 'Pause (F2)', \&pause, ' Exit (F3) ', \&sig_handler, 'Debug(F4)', \&toggle_debug, 'Log(F5)', \&toggle_log);
tk_checkbutton adds a Tk checkbutton widget to the mh tk grid. It will accept multiple variables, displaying them in a row in the grid.
Usage: &tk_checkbutton('text', \&var1); &tk_checkbutton('test1', \&var1, 'text22', \&var22, 'text3', \&var33);
Examples:
&tk_checkbutton('Debug on', \$config_parms{debug}); &tk_checkbutton('Sleeping Parents', \$Save{sleeping_parents}, 'Sleeping Kids', \$Save{sleeping_kids});
Use this function to allow for arbitrary data to be entered via the mh tk grid.
Usage: &tk_entry('Entry label:', $state_based_object); &tk_entry('Entry label:', \$variable); &tk_entry('Entry label:', \$variable, 'Entry label2:, \$variable2);
Example: &tk_entry('Sleep time:', \$Loop_Sleep_Time); &tk_entry('Test in 1', \$Save{test_input1}, 'Test in 2', \$Save{test_input2});
Note: The $variable reflects the data, as it is being entered. If you want to test on the data only after the RETURN key has been hit, use %Tk_results array. The $variable is copied to $Tk_results{'Entry label:'} only after the RETURN key has been entered.
Now you can now also use a state based object (like Generic_Item) to store/monitor/change the tk_entry text.
Examples:
&tk_entry('TV search', \$Save{tv_search}); if ($state = $Tk_results{'TV search'}) { run qq[get_tv_info -times all -keys "$state"]; set_watch $f_tv_file; undef $Tk_results{'TV search'}; }
$mp3_search_text = new Generic_Item; $mp3_search_text -> tie_event('print_log "mp3 search text is now $state"'); &tk_entry('mp3 Search', $mp3_search_text);
Use these functions to add a Tk label widget to the mh tk grid (tk_label) or the tk menu_bar (tk_mlabel). &tk_label will accept multiple variables, displaying them in a row in the grid.
Usage: &tk_mlabel(\$variable); &tk_label(\$variable); &tk_label(\$variable1, \$variable2, \$variable3);
Example:
&tk_mlabel(\$Save{email_flag}); $Save{email_flag} = file_read "$Pgm_Root/data/get_email.flag";
Use this function to create radio buttons in the mh tk grid. If labels are not specified, the values are displayed.
Usage: &tk_radiobutton('Button label:', $state_based_object, ['value1', 'value2', 'value3']); &tk_radiobutton('Button label:', \$variable, ['value1', 'value2', 'value3']); &tk_radiobutton('Button label:', \$variable, ['value1', 'value2', 'value3'], ['label1', 'label2', 'label3']);
Examples:
&tk_radiobutton('Mode', \$Save{mode}, ['normal', 'mute', 'offline']); &tk_radiobutton('Debug', \$config_parms{debug}, [1, 0], ['On', 'Off']); &tk_radiobutton('Tracking', \$config_parms{tracking_speakflag}, [0,1,2,3], ['None', 'GPS', 'WX', 'All']);
my $alarm_states = "Disarmed,Disarming,Arming,Armed,Violated,Exit Delay,Entry Delay"; my @alarm_states = split ',', $alarm_states; $alarm_status = new Generic_Item; &tk_radiobutton('Security Status', $alarm_status, [@alarm_states]);
$v_alarm_status = new Voice_Cmd "Set the alarm to [$alarm_states]"; $v_alarm_status -> tie_items($alarm_status);
print_log "Alarm status changed to $state" if $state = state_now $alarm_status;
See mh/code/examples/tk_examples.pl for more tk_* examples.
This function will retrieve data from the web (http web pages or ftp files). If the web page is large or the site is slow, you may want to use a Process_Item call to the get_url program, so the request can be done as a separate process and mh will not get hung up while waiting.
Examples:
my $html = get 'http://marketing.cbs.com/lateshow/topten';
Other examples are in mh/code/bruce/internet_data.pl
Returns the numeric IP address of the specified hostname. If hostname is blank, localhost is used. If used in a list context, all associated IP address are returned (e.g. dial up address and local address).
Examples:
print_log "Current IP address " . get_ip_address;
# Echo dynamic IP address to the Tk gui if ($New_Minute and net_connect_check) { $Tk_objects{ip_address} = "IP address: " . get_ip_address; } tk_label(\$Tk_objects{ip_address});
Un-escapes ``%xx'' data back into the original characters. Use on HTML FORM data.
This will return the domain_name of the last client to access the specified port. If the mh.ini DNS_server parm is NOT set, it will return the IP address instead. You can also pass it a IP address, instead of a server port name.
If used in an array context, it returns the full domain name, and a short version of the domain name (e.g. 'www.ibm.com' and 'ibm').
Examples:
my $domain_name = &net_domain_name('http'); my ($name, $name_short) = &net_domain_name('server_speak'); my ($name, $name_short) = &net_domain_name('204.146.18.33'); Restults $name = 'www.ibm.com', $name_short = 'ibm';
Checks to see if an IP address is available. Returns true if pingable.
Example: &ping($host); # Default protocol is specified in mh.ini &ping($host, $protocol);
Used to ftp data to or from an ftp server. There is also a stand alone version of this command in mh/bin/net_ftp, so you can run this command with a run or a Process_Item to avoid hanging mh with long running ftp sessions.
Usage:
net_ftp(option1 => $value1, option2 => value2 ...); These are the possible options:
server Default is mh.ini parm net_www_server user Default is mh.ini parm net_www_user password Default is mh.ini parm net_www_password dir Default is mh.ini parm net_www_dir file Name local/remote file to get/put file_remote Name of remote file (if different from local file) command get/put/delete. type ascii/binary (default is ascii)
Example:
net_ftp(file => 'index.html', command => 'put');
my $rc = net_ftp( file => 'c:/junk1.txt', file_remote => 'incoming/junk1.txt', command => 'put', server => 'misterhouse.net', user => 'anonymous', password => 'bruce@misterhouse.net'); print_log "net_ftp put results: $rc";
$v_test_ftp = new Voice_Cmd 'Test background ftp [get,put]'; $p_test_ftp = new Process_Item; if ($state = said $v_test_ftp) { set $p_test_ftp "net_ftp -file c:\junk1.txt -file_remote incoming/junk1.txt " . "-command $state -server misterhouse.net " . "-user anonymous -password bruce\@misterhouse.net"; start $p_test_ftp; } print_log "Ftp command done" if done_now $p_test_ftp;
Returns true if connected to the Internet. If the mh.ini parm net_connect=persistent, this always returns true.
Used to send and AOL Instant Message or Jabber message across the internet.
Information on AIM clients (available for various different platforms) can be found at http://www.aol.com/aim/faq/getstarted.html . Tik, a tk based client for use on a Unix os, can be found at http://tarp.linuxos.org/tik . You can register for an AIM name at http://www.aol.com/aim/faq/registration.html .
Information on Jabber clients (many open sourced clients are available for different platforms AND the protocol is open) can be found at http://www.jabber.com and http://www.jabber.org.
Usage: net_jabber_send(option => value); net_im_send(option => value);
These are the options:
password Default is mh.ini parm net_jabber_password or net_aim_password from Default is mh.ini parm net_jabber_name or net_aim_name to Default is mh.ini parm net_jabber_name_send or net_aim_name_send server Default is mh.ini parm net_jabber_server (e.g. jaber.com) resource Default is mh.ini parm net_jabber_resource (this can be left blank)
text Message file Message. You can use the text and/or file options.
The first time you send a message, mh will pause for a few seconds while it logs onto AIM or a jabber server. Subsequent messages use the same signon, so are sent much faster.
Once you have been logged in, mh will also display incoming jabber messages. This code has lots of other possibilites that can be added, since jabber is XML based and very flexable.
Example:
net_jabber_send(text => "Stock summary\n $Save{stock_data1}\n $Save{stock_data2}")
net_im_send(text => "Internet mail summary for $Date_Now $Time_Now", file => "$config_parms{data_dir}/get_email2.txt") if time_cron '05 12 * * 1-5';
Used to send email. Usage: net_mail_send(option
=> value);
These are the options:
server Default is mh.ini parm net_mail_ACCOUNT_server from Default is mh.ini parm net_mail_ACCOUNT_address to Default is mh.ini parm net_mail_ACCOUNT_address
account This is the ACCOUNT field used in finding the above parms. It defaults to the mh.ini parm net_mail_send_account.
subject Default is 'Email from Mister House' text Body of the message file File with the body of the message.
mime Set to 1 if you want mail sent in mime format (e.g. file has html) baseref If sending html with mime => 1, use this to set the BASE HREF
Example:
net_mail_send(text => "Test email sent at $Time_Now\n\n");
net_mail_send(account => 'Bruce', to => 'winter@misterhouse.net', text => $msg);
Returns the number of email message on the specified account.
Usage: net_mail_count(option => value);
These are the options:
server Default is mh.ini parm net_mail_ACCOUNT_server user Default is mh.ini parm net_mail_ACCOUNT_user password Default is mh.ini parm net_mail_ACCOUNT_password
account This is the ACCOUNT field used in finding the above parms. It defaults to the mh.ini parm net_mail_send_account.
Example:
my $count = net_mail_count(account => 'Bruce'); speak "Email account Bruce has $count new email messages";
This returns a pointer to a hash array containing info on mail for a specified email account. Check out the mh/bin/get_email program for a usage example. Rather than call this with mh code directly, calling get_email with a Process_Item, so mh does not pause while email is being checked. See mh/code/test/internet_mail.pl for an example.
Returns a list array of pointers to data read from an email account. This is not tested and needs to be documented.
These programs are included in mh mh/bin directory. Most of these are meant to be called from mh events, as separate processes, but they can also be run stand alone. You can get the help text for most of these programs with the -h option (e.g. get_tv_grid -h).
alpha_page sends alphanumeric page.
Usage:
alpha_page [options]
-h => This help text -help => This help text -name xxx => name of recipient (must be defined in mh.ini) -pin xxx => pin number -message xxx => text of message to send
Example: alpha_page -pin 123456 -message 'Bring home bread and milk' alpha_page -name craig -message 'Bring home bread and milk'
Use this to display a string of text or the contents of a file using a Tk window.
Usage: display -options text_or_file
Options: -time xxx to have the window auto-close in xxx seconds. Set to 0 for no auto-close. -font xxx to pick the font -title xxx to set the window title
Examples: display weather_forcast.txt display -title 'triva answer' c:/data/triva_answer.txt display "Remember to take out the garbage" -time 0
Use this to display a Tk window with a list of the incoming and outgoing phone logs. An example of how to create these logs is in mh/code/bruce/phone.pl
Usage: display_callers (no argument -> uses latest logs) display_callers 1997_11 (Look only at November, 1997 logs)
get_ceiva gets pictures from the 'now showing' folder of a Ceiva.com web page.
Usage: get_ceiva [options]
-h => This help text
-username xyz => xyz is a ceiva member name -password xyz => xyz is a ceiva password -frame_id xyz => xyz is a ceiva frame_id
-outdir xyz => xyz the directory the pictures will be put in -browser xyz => xyz is the path to your html browser -reget => Re-fetch the files, even if they look current
Examples: get_ceiva -outdir /pictures/ceiva get_ceiva -reget
Use this program to check all the email accounts defined in your mh.ini file. It will create the following files:
mh/data/get_email.data It uses this database so see what email is new since it was last called. To get a complete list of all email (not just new/recent mail). delete this file before running get_email
mh/data/get_mail.txt This summarizes how much mail there is and from who. Here is an example of what the file might look like:
Email account bruce has 2 new email messages from John Doe and Bill Gates
mh/data/get_mail.flag This has a string of digits, one for each account, that shows how much mail is each account.
You can create a get_email_rule.pl file to control how this program summarizes mail. See get_email_rule_example.pl for an example.
Here is an example of how to call it from mh (see mh/code/test/internet_mail.pl for the complete example):
$p_get_email = new Process_Item('get_email'); start $p_get_email if !$Save{sleeping_parents} and $New_Minute and !($Minute % 20) and &net_connect_check;
if (done_now $p_get_email) { my $email_text = file_read "$Pgm_Root/data/get_email.txt"; speak "rooms=all $email_text" if $email_text; $Save{email_flag} = file_read "$Pgm_Root/data/get_email.flag"; } &tk_mlabel(\$Save{email_flag});
get_mp3_data reads mp3 directories and stores the results in a dbm file. This data is used by mh/test/mp3_playlist/pl to search/play mp3 tag data, file names, and playlist files.
Usage:
get_mp3_data [options] dir1 dir2 etc
-h => This help text -help => This help text
-dbm xyz => Stores the data in dbm file xyz.
Examples: get_mp3_data c:\mp3 d:\mp3 get_mp3_data -dbm e:\mh\data\mp3_dbm c:\mp3
get_tv_grid gets a TV grid/schedule from the web (clicktv.com) and changes so it to be used by the MisterHouse program to create VCR and TV event reminders. See mh/code/bruce/tv_grid.pl for a mh event example that uses get_tv_grid.
Usage: get_tv_grid [options]
Options: -h => This help text -help => This help text
-userid xyz => xyz is your clicktv userid. Go to http://www.clicktv.com to create one, or to find the generic one that matches your local tv schedule.
-day xyz => xyz is the day to get/filter. Default is today. -hour xyz => xyz is the hour to get/filter. Default is 6pm. Can also be 'all' to get all hours.
-days xyz => xyz is the number of days to get/filter, starting with -day.
-channels xyz => xyz is the number of channels to filter. Default is 999.
-infile xyz => xyz is original input file. Default is web/tv/clicktv/day_hour.html If this file is missing or old, a new file will be retreived from the web.
-outfile xyz => xyz the filtered output file. Default is -outdir/day_hour.html
-outdir xyz => xyz the directory the outfiles will be put in. Default is mh.ini parm html_dir/tv
-label xyz => Use xyz as the link lable. Default is "Set the Vcr"
-reget => Re-read the clicktv web page, even if a recent file it already exists. -redo => Re-write -outfile xyz, even if it already exists.
-debug => turn on debug info
-mail_to xyz => Will email the charts to xyz -mail_server xyz => xyz is the SMTP host. Default is localhost -mail_baseref xyz => xyz is the http address of your mh server. Needed if you want to control mh from the emailed web page
Examples: get_tv_grid -day 25 -hour 4pm -outfile my_tv.html get_tv_grid -days 7 -hour all get_tv_grid -email bruce\@misterhouse.net -mail_baseref misterhouse.net:8080
get_tv_info returns info about tv programs that match the requested parms. It uses a database created by the get_tv_grid program. See mh/code/bruce/tv_info.pl for examples on how to use this from mh.
Usage: get_tv_info [options]
Options: -h => This help text -help => This help text
-channels xyz => Will return info only for channel numbers xyz. Default is all the channels found by get_tv_grid. -dates xyz => Will return info only for dates xyz. Default is today. Format: month/day (e.g. 4/22). -time xyz => Will return info only for shows that start at xyz. Default is '6pm-10pm'. Use 'all' for all hours. Valid formats: 1 pm, 1PM, 13, 13:00. -lengths xyz => Will return info only for shows that are xyz hours long. Default is any length. -keys xyz => Will return info only for shows that have keywords in the xyz list in their title or description.
All of the above parms support these formats: : comma-seperated values (e.g. -dates 7/4,7/5,7/6) : - delimited ranges (e.g. -dates 7/4-7/6) : + adder spec (e.g. -dates 7/4+2) Starting spec is optional (e.g. -dates +2)
-debug => turn on debug info
Examples: get_tv_info -channels "4-12" -lengths 2 get_tv_info -channels "4,6,12" -time "7 pm" get_tv_info -dates "7/4-7/11" -keys "star trek,dilbert" get_tv_info -dates +14 -keys computer
get_url gets a web page and echos it to STDOUT or a local file.
Usage: get_url url [local_file]
If local_file is specified, data is stored there, otherwise it is echoed to STDOUT.
Examples: get_url http://marketing.cbs.com/lateshow/topten/ $f_top10_html;
get_weather gets weather info NOAA web servers.
Usage: get_weather [options]
Options: -h => This help text -help => This help text
-city xxx => xxx is the City you want. -state xxx => xxx is the State you want.
-data xxx => xxx is either conditions, forecast, or all. Default is all.
-refresh xxx => xxx is the number of minutes old the cached data can before it will be refreshed from the net. The default is 60 minutes.
-no_log => Unless this option is used, the results also get filed into the mh/data/web directory
Example: get_weather -city Rochester -state MN
Here is an example mh event for calling get_weather as a background process using the 'run' function:
$v_get_internet_weather_data = new Voice_Cmd('Get internet weather data'); $v_show_internet_weather_data = new Voice_Cmd('Show internet weather data'); if (said $v_get_internet_weather_data) { run "get_weather -city $config_parms{city} -state $config_parms{state}"; set_watch $f_weather_forecast; } if (said $v_show_internet_weather_data or changed $f_weather_forecast) { print_log "Weather data displayed"; display name $f_weather_forecast; display name $f_weather_conditions; }
This is a simple shell that echos whatever you type into the 'xcmd_file'. The name of the 'xcmd_file' is specified in mh.ini. If mh detects this file, it will read and execute its contents, then delete it.
Examples: house speak hi there house display c:\autoexec.bat house Turn the backyard light on house XAJAK
You can use mhl to start mh (rather than calling mh directly) to help ensure that mh will always be running. This is a simple shell will restart mh if it detects that mh exited abnormally. You can pass in the same startup parms to mhl as you us for mh.
net_ftp sends, receives, or deletes a file to remote site via ftp. There is also an identical net_ftp function, if you want to call it directly from mh.
Usage: net_ftp -option value -option value ...
Where -option can be any of the following server Default is mh.ini parm net_www_server user Default is mh.ini parm net_www_user password Default is mh.ini parm net_www_password dir Default is mh.ini parm net_www_dir
file Name local/remote file to get/put file_remote Name of remote file (if different from file) command get/put/delete. type ascii/binary (default is ascii)
Example: net_ftp -command put -file junk1.txt -file_remote /tmp/junk1.txt
outlook_read reads MS Outlook (a windows mail/calendar program) folder data and optionally creates a mh code file to implement calendar events.
Usage: outlook_read [options]
Options: -help -> help text
-version xyz -> Version of Outlook. Use 98 if you have Outlook 98.
-quiet -> do not echo data to STDOUT -debug -> print out debug
-folder xyz -> Get data from folder xyz. It can be one of the following: Deleted, Outbox, SentMail, Inbox, Calendar (default), Contacts, Journal, Notes, Tasks
-pl_file xyz -> Write out a mh perl code file. These are the various formats of Calendar subjects:
vcr channel_num show_name (e.g. VCR 8 Dilbert) voice_command (e.g. Christmas lights on) message_to_speak (e.g. Today is national geek day)
Note: If the text is not a vcr or voice_command, it will be treated as a messages.
-date xyz -> Get data with a start_time on date xyz. Default is today. -date_end xyz-> Get data with a start_time between -date and -date_end -days xyz -> Look out xyz days from -date. Default is 1 day.
Examples: outlook_read -help outlook_read -date 1/17 outlook_read -date 12/25/97 -days 2 outlook_read -pl_file /projects/mhcode/outlook_events.pl outlook_read
report_weblog reads MisterHouse and/or Apache server logs and generates report graphs and optionally email them.
Usage:
report_weblog [options] logfile(s)
-h => This help text -help => This help text
-ignore xyz => A comma-delimited list of ip address to ignore.
-mailto xyz => Will email the charts to xyz -mailserver xyz => xyz is the SMTP host. Default is localhost
-runid xyz => All graphs will have xyz as a prefix. Default is blank. -outdir xyz => All graphs be stored in directory xyz. Default is .
Examples: report_weblog -mailto 'bruce@misterhouse.net' /var/log/httpd/access_log report_weblog -mailto winters@home.net -mailserver 24.2.1.70 e:/mh/data/logs/server.1999_07.log
mhsend allows you to send data to mh over the intra/internet. Data can be logged, filed, spoken, displayed, or run. The data processed by mh/code/test/mhsend_server.pl
mhsend sends data to the MisterHouse program, through the internet/intranet.
The following flags control what the companion mh server.pl code does:
-file xxx -> Files the data into mh/data/mhsend/xxx
-log xxx -> Logs the data into mh/data/mhsend/xxx.log
-run -> Runs the data as command.
-display xxx -> Displays the data. xxx is how log to leave the display up.
-speak -> Speaks and displays the data.
-pwfile xxx -> Points to a password file.
Usage: mhsend 'hi there' mhsend -display 60 hi there Bruce mhsend -port 8083 -host misterhouse.net 'hi there' mhsend -speak file_to_speak.txt mhsend -file file1 file_to_send.txt mhsend -log This is a good url: http://goodplace.com echo 'hi there' | mhsend -stdin
monitor_weblog monitors a Apache server log file. When the server logs hits, this code will summarize them and pass them onto the MisterHouse speak_server.pl code via a tcp/ip socket.
Usage:
monitor_weblog [options] logfile
-h => This help text -help => This help text
-mh_server xyz => The ip address of your MisterHouse box. -mh_port xyz => The ip port you set mh.ini server_speak to. -ignore xyz => A comma-delimited list of ip address to ignore.
Examples: monitor_weblog -mh_server house -ignore 'house,10.0.0.1' /var/log/httpd/access_log
set_clock sets the clock according to the time from an internet connected NIST atomic clock server. Instead of requiring accurate time zone information, set_clock will simply set the time minute and second, but will keep to the nearest hour it was already set to.
Usage: set_clock [options]
Options: -h => This help text -help => This help text
-no_set => Do NOT set the clock, only list the difference in time.
-ignore xyz=> Do NOT reset the local clock if the time is more then xyz minutes off. Default=10
-method xyz=> xyz can be inet_time, http, or socket. Default is socket.
-server xyz=> xyz is the server to get the clock data from. Here are a few: time-a.timefreq.bldrdoc.gov:14 (default) time.nist.gov:?? time-nw.nist.gov:??
Example: set_clock set_clock -server time-a.timefreq.bldrdoc.gov:14
More info about NIST clock servers if available at: http://www.boulder.nist.gov/doc-tour/atomic_clock.html
Here is an example call from mh. Since this is typically quick, we do it here with a 'do' instead of a call with Process_Item:
if (time_cron '7 6 * * * ') { my $status = do "$Pgm_Path/set_clock"; speak $status unless $Save{sleeping_parents}; }
set_password creates and/or queries the mh password file, using the crypt function. If this password is set, then web and telnet access will prompt for it. To reset the password, delete the mh/data/.password file.
Usage: set_password [options]
Options: -h => This help text -help => This help text
-password xxx => xxx is the password to check. If not used, a TK popup window will prompt for it.
-pw_file xxx => xxx is the file that the crypt-ed password is read/written to. Default is mh/data/.password
Example: set_password
speak will pass the file or text to mh. It simply runs the house program. Example: speak ``Boo, did I scare you?''
=item trivia
This will update trivia question and answer files from the specified database. See mh/code/bruce/trivia.pl for an example of how to call it from mh.
A one way crypt-ed password authentication scheme can be enabled for the web and telnet.pl interfaces.
The location of the crypt-ed password is controlled with the mh.ini password_file parm. The default location is in $Pgm_Root/data/.password.
If this file does not exist, the telnet.pl and web interface will be enable for anyone and everyone. If it does exist, and the correct password is not entered, the telnet.pl and web function will not implement the requested commands.
The set_password command is used to create and check this password. If you are running the un-compiled perl and do not have Tk installed, the password will have to be passed in with the -password parm (set_password -h for more help). This can be run from a shell prompt or with the mh mh_control ``set the password'' command.
If you want to change the password, delete the password file and rerun set_password.
When prompted for UserName and Password from your web browser, you can leave UserName blank ... it is not used.
Netscape appears to allow you to re-try a password, but MS Explorer must be re-started if you want to enter a different password.
You can modify what the Tk window displays by modifying the tk_frame.pl and tk_widgets.pl members. tk_frame.pl controls the general layout and size of the various scrolled lists. Modifying this member takes a little knowledge of perl Tk.
tk_widgets.pl shows how you can add widgets to display various info and allow for buttons for manual control. The mh tk_xxx subroutines used here will add widgets to either the menu_bar or to the 'grid' frame defined in tk_frame.pl.
Any change to tk_frame.pl or tk_widgets will be evaluated on a code reload, as will the tk_geometry parm in mh.ini.
tk_xxx widgets can be added anywhere in your mh code, not just the tk_widgets.pl member. See the weather_monitor.pl, internet_mail.pl, and tk_eye.pl members in mh/code/bruce for examples.
The tk_xxx widgets are 'packed' into the menu_bar or grid in the order that they are evaluated. You can use the 'Position' directive at the top of your member.pl code to control this. Position=1 is reserved for the tk_frame.pl member, as this must be evaluated first.
Note, you can also use native Perl tk commands, or create or override the tk_xxx subroutines in your own code for more flexibility.
For more info on the tk_xxx widgets, see look in the mh functions section of this document.
You can write your own Web page interface. The examples under mh/web are frame html files that dictate the shape and positions of various mh-generated html frames.
You can use the html_root and html_file mh.ini parms to point to the directory and default web page. The html_default parm are the member names used when the URL points to a directory.
The html_style mh.ini parm points to a style sheet that will be loaded for all mh-generated pages. Style sheets allow default control of colors and fonts.
There are other, html_* parms in the mh.ini file to control how the auto-generated html data looks. For example, you can control table sizes and auto refresh rates. See the mh.ini file for more info.
The web Items and Groups displays will use icons to indicate the object state. Icons will be searched for in the mh/graphics directory according to the objects icon method, the object name, the object state, and the object type. For example, given this code:
$fountain = new X10_Appliance('C3'); set_icon $fountain 'water';
The search order for the ON state will be:
water-on.gif fountain-on.gif x10_appliance-on.gif on.gif
The web Category display will try to match icons to Voice_Cmd objects using the icon method, object name, voice command text, and filename. For example, given this code from the member internet_data.pl:
$v_set_clock = new Voice_Cmd('Set the clock via the internet'); set_icon $v_set_clock 'time';
The search order will be:
Look for icons that match time Look for icons that match set_clock Look for icons that match words in 'Set the clock via the internet' Look for icons that match internet_data.pl
All icons in the graphics directory are compared to each of the above. The first match (by search order) is used. If there are multiple matches, the match with the longest word is used. For example, if we had icons names 'clock.gif' and 'internet.gif' (but no 'time.gif'), 'internet.gif' would be used.
In all of the above examples, .gif was used as an example, but any browser compatable graphics file can be used (e.g. .png and .jpg). If you want to add new icons, some good sources are at http://www.freegraphicland.com/ , http://www.graphsearch.com/ , http://www.add-soft.com/icons/index.html , and http://www.rad.kumc.edu/icons/icons.htm
Important note: If you do play with adding/deleteing/renaming icons, you will want to do a reload before refreshing your browser. The icon directory is cached by mh (for efficiency). If no code has changed, the reload will run very quickly.
You can use the following html addresses to have mh return dynaically generated html:
/category This will list all the Voice_Cmds, sorted by categories. Categories are the member names of the user code, or the value in the Category=value field specified in the user code. Items are also listed by Group, and Object Type
/list?Category This can be used to list just the commands in the specified Category.
/group This will return all the Group Items
/group?$group_name This will return all the objects in $group_name
/items This will return all the non Voice Objects (e.g. X10_Items)
/items?object_type This will return all the objects of type object_type
/widgets This lists all the Tk widgets.
/widgets_label This returns just the label widgets
/widgets_entry This returns just the entry widgets
/widgets_radiobutton This returns just the radiobutton widgets
/widgets_checkbox This returns just the checkbox widgets
/speech This lists the most recently spoken text
/print_log This lists the most recently print_log text.
If you want to generate your own html on-the-fly (like a cgi program would), instead of pointing to a .html file or or one of the above pre-defined mh-generated lists, point to a .pl file and have that perl code return the desired html. Note, this currently differs from how a classic cgi program would work (they return the html as STDOUT). Some examples of this can be found in the mh/web/test/*.pl files.
One other note on .pl web files. You can have them use variables you define in your mh user code, but only if those variables are defined with ``use vars '$my_var1', '$my_var2'''. Variables defined with ``my ($my_var1, $my_var2)'' are local only to the mh loop, and would not be available to any web .pl program.
In addition to calling the above mh-generated html directly, you can embed these lists in your own html using the server-side-include syntax. If the extention on your html is .shtml (for server-side html) then mh will parse the html, looking for the following string:
<!--#include file="your_directive"-->
Anytime the above string is found, it will replace that record with whatever you specify in ``your_directive''. ``your_directive'' can either be another html (or shtml) file, or it can be one of the mh-generated lists from above. For example:
<!--#include file="/mh/other_links.html"--> <!--#include file="category"--> <!--#include file="/mh_default/test/test1.pl"-->
You can use an #include var= directive, like file= above except it returns the contents of an mh variable. For example:
<li><b>Version:</b> <!--#include var="$Version"-->
This what is used in the mh 'About' web button to return the mh version.
You can also define your own functions in your code files to return html, and then call them with the code= include directive. For example, add these to any of your code files:
sub web_func1 { return "uptime = " . &time_diff($Time_Startup_time,$Time , undef, 'numeric'); } sub web_func2 { return "results from function 2"; }
Then call them like this:
<p>Test web functions <!--#include code="&web_func1"--> <!--#include code="&web_func2"-->
One useful built in function is &dir_index, which can be used to create a directory listing of files in selected directories. For example, if you have a web directory called pictures, create an index.shtml file, and include the following line: <!--#include code=``&dir_index('/pictures','name',0)''-->
You can have &dir_index
sort by name, type, size, or date.
Set the 3rd argument to 1 for reverse sort order. See
mh/web/graphics/index.shtml for an example.
If you want to have only a few specific html controls, you can write your own html using the RUN, SET, and SET_VAR url keywords to control mh. Here are a few examples:
<a href=http://localhost:8080/SET?$test_lights?off >Turn the test light off</a> <a href=http://localhost:8080/SET_VAR?$Save{test_input1}?abc >Load test_input1 with abc</a> <a href=http://localhost:8080/RUN?WebCam_light_on >WebCam light on</a>
The SET command can set any object defined in your user code to a valid state. The SET_VAR command can set a variable to any arbitrary value. The RUN command can run any voice command. Use '_' to replace blanks in the command string.
SET, SET_VAR, and RUN can use an optional response field to decide what html will be returned:
Format: SET_VAR:h_response?$var?value SET:h_response?object?value RUN:h_response?command
Where h_response can be one of the following: &function: The results of &function will be returned &function(arg): The results of &function(arg) will be returned last_response: The last displayed or spoken text. last_displayed: The last displayed text will be returned last_spoken: The last spoken item will be returned filexyz.html: The contents of filexyz.html will be returned string: string will be returned blank: A list of recently spoken text will be returned
Here are some examples demonstrating these options:
This returns the last spoken line: <li><a href=http://localhost:8080/RUN:last_spoken?WebCam_light_on >Light on</a>
This returns the string "Thanks for playing" <li><a href=http://localhost:8080/RUN:Thanks_for_playing?WebCam_light_off >Light off</a>
This returns the contents of the mh/web/test/test3.html file: <li><a href=http://localhost:8080/RUN:test/test3.html?WebCam_light_+50 >+50</a>
This returns a list of recently spoken text: <li><a href=http://localhost:8080/RUN?WebCam_light_-50 >-50</a>
This will allow for text input, using a html FORM, and returning the the results form the user defined function &my_response:
<FORM ACTION="SET_VAR:&my_response">Test 1<INPUT SIZE=5 NAME="$Save{test_input1}"</form>
You can also use RUN:h_response without a command. For example, if you want to get a directory listing, sorted by date (recent on top):
<a href=http://localhost:8080/RUN:&dir_index('/pictures','date',1)>List pictures by date </a>
VR and TTS options are controlled with the Microsoft Voice application. It shows up as a 'green V' in your icon tray. Right clicking on this icon brings various menus for configuring your VR and TTS options.
You can pick between 3 VR modes, by right clicking on the MS Voice icon, or toggle between the modes by left clicking on the icon.
- Listening for Voice Commands
This mode will listen to anything and everything. You can get lots of false recognition in noisy environments in this mode.
- Not listening
In this mode, VR will be turned on only when you hold down an activation key or move the mouse to a screen corner, depending on which 'Listening Mode' you pick in the 'Voice Command Options' menu.
- Paused listening
This mode only listens for a specific trigger keyword. If heard, then it temporarily switches to normal listening mode. The trigger keyword is listed as your 'Computer Name' in the 'Voice Command Options' menu.
You can improve your Voice Recognition accuracy by talking through a 10 minute training session. Right click on the MS Voice icon and pick 'Voice Command Options', then click on the 'Advanced' tab, and finally click on the 'Optional Training' button.
You can pick the voice and speed of the TTS by clicking on the 'Computer Voice' tab on the same 'Voice Command Options' dialog.
Need notes on how to use viavoic on linux and how to enable different TTS engines with Festival.
For vr with viavoice, use mh/code/bruce/viavoice_control.pl
This section has info that doesn't fit well anywhere else. Probably be better as a FAQ.
My hope is that mh will grow into a group project. There is so many, almost unlimited possibilities in Home Automation, that no one person could hope to implement them all.
If you feel so inclined, please send me any code that you develop that you think others might be interested in, and I'll include it in the distribution.
If you would rather see it mh in your windows tray, rather in the taskbar, here is a useful utility that will let you move any program to the tray: http://www.teamcti.com/TrayIt
Another handy utility for windows 95/98 users will help you monitor how much cpu and memory mh takes. TaskInfo80, available at http://www.iarsn.com/index.html
If you are getting the message ``Out of environment space'' try adding these records:
config.sys: shell=command.com /e:8000 /p autoexec.bat: set comspec=c:\command.com
If you want to use a program generated list in an Item declaration, you need to use add # noloop=start/stop comments before and after the code that should preceed the Item. Here is an example:
# noloop=start my $mp3names; while ( my $mp3name = <d:/library/*.m3u> ) { $mp3name =~ s#^.*/##; # remove path $mp3name =~ s#\..*$##; # remove extension $mp3names .= "," if $mp3names; $mp3names .= $mp3name; } # noloop=stop
$v_play_music = new Voice_Cmd("Play [$mp3names]"); if ($state = said $v_play_music) { ... more code here ...
If you want to have a user defined function called once perl loop, but you
want it before or after all the other user code has been called, use the
&MainLoop_pre_add_hook
and
&MainLoop_post_add_hook
functions to add callbacks to your
functions. For example:
$v_hook_pre_add = new Voice_Cmd 'Add pre code hook'; $v_hook_pre_drop = new Voice_Cmd 'Drop pre code hook'; $v_hook_post_add = new Voice_Cmd 'Add post code hook'; $v_hook_post_drop = new Voice_Cmd 'Drop post code hook';
&MainLoop_pre_add_hook( \&test_hook_pre) if said $v_hook_pre_add; &MainLoop_pre_drop_hook( \&test_hook_pre) if said $v_hook_pre_drop; &MainLoop_post_add_hook( \&test_hook_post) if said $v_hook_post_add; &MainLoop_post_drop_hook(\&test_hook_post) if said $v_hook_post_drop;
sub test_hook_pre { print "<"; } sub test_hook_post { print ">"; }
If you want your hook code to last between code reloads (e.g. added by startup code from a module), set the persistant flag by passing a 1 as a 2nd argument. For example:
&main::MainLoop_post_drop_hook( \&jabber::process, 1 );
Here is a list of all the code hook locations:
MainLoop_pre => Run before user code MainLoop_post => Run before user code Serial_match => Run when incoming serial data matches Play_pre => Run before playing wav files Play_post => Run after playing wav files Speak_pre => Run before speaking text files Speak_post => Run after speaking text files
For more examples on code hooks, see mh/code/test_code_hooks.pl
The JDS and Marrick interface modules are untested.
mh also has the the 'windows 49.7 day bug' (2**32 milliseconds) that has been in the news lately: http://www.news.com/News/Item/0,4,33117,00.html?st.ne.ni.rel .
Under windows, TK windows do not get focus and some keys and shortcuts do not work. Worse yet, the 'tear off' line (the dashed line on the pull down menus) causes mh to hang. Perl/tk on unix does not have either of these problems. Hopefully this will get fixed in a future release of perl/Tk. Note: if you have an old perl/tk (older than 8.0012), things are even more messed up.
If you have perl installed, you may get this message:
Can't find 'boot_IO' symbol ...
The problem is there are two different IO.DLL files, one in the FTP module and one in the TK module. Do a find in IO.DLL in your perl directory, and rename the one in the tk path.
If 'run' calls give you an 'out of environment memory' error, create a command.pif and change the memory, initial environment from auto to 4k.
Activestate perl 5.6 build 613 and 616 leaks memory. On my box about 30 Meg, once a day. Displaying .gif and .jpg files also leaks about 10x the picture size (e.g 10 50k photos -> 5 meg).
Some people are getting CRC errors when running iButton code under Windows NT.
Adding, deleting, or changing Voice commands does is not recognized by the Windows MSVoice applicatino while it is running. You have to exit and restart MSVoice, then restart mh to get it to recognize new commands. Hopefully we can get this to work with release 5.0 of the MS Speech SDK.
Voice Recognition IS supported under linux using IBM's ViaVoice SDK. Potential candidates for VR engines other non-linux unix platforms are:
http://www.speech.cs.cmu.edu/sphinx/ http://cslu.cse.ogi.edu/index.html http://WWW.ISIP.MsState.Edu/projects/speech_recognition/ http://www.tmt.de/~stephan/ears.html
The control keys from the console window do not work (e.g. F1 to reload, F3 to exit). Ctl-C works :)
Disk drive info (need to port from DriveInfo to df)
Linux KDE users should uncheck ``Apply fonts and colors to non-KDE apps'', or the black text on white windows shows up as white text.
Bruce Winter bruce@misterhouse.net http://misterhouse.net
mh can be download from http://misterhouse.net
Here is a list of other online mh sites:
http://www.archer-online.net
Here is a list of articles written about MisterHouse:
http://www.hometoys.com/htinews/jun99/articles/winter/winter.htm
http://linuxworld.com/linuxworld/lw-1999-10/lw-10-mrhouse.html
http://www.faribault.k12.mn.us/brian/aprs/
http://www.circuitcellar.com/pastissues/articles/winter111/winter.pdf
http://www.itknowledge.com/tpj/issues/vol5_1/tpj0501-0006.html (User login to The Perl Journal is required)
The Circuit Cellar Ink article includes a diagram of how to set up a house PA system, so you can control which rooms MisterHouse talks to.
You can suscribe and/or view the mailing list at http://sourceforge.net/mail/?group_id=1365 You can view the archive at http://lists.sourceforge.net/pipermail/misterhouse-users/ and search it at http://lists.sourceforge.net/mailman/listinfo/misterhouse-users
An older list with a searchable archive at <a href=http://www.onelist.com/messages/MisterHouse>here</a>).
Jan Dubois has a fun page of other ticks you can do with perl on windows at http://opensource.activestate.com/authors/jandubois/Perl/TPC3/fun.html
The comp.home.automation newsgroup is another handy resource, especially for X10 related questions.
Neil Cherry has created a home for linux related Home Automation programs at http://linuxha.sourceforge.net . This project is developing daemons for various interfaces (e.g. ADI Ocelot, CPUXA, and HCS II) that hopefully we can then link to with mh.
Rene Mueller has a nice set of web pages with lots of info on HomeAppliances at http://the-labs.com/HomeAppliance/
Copyright (C) 1998-2000 Bruce Winter. All rights reserved.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU public license.
This program will most likely not cause your house to self destruct, but if it does, please don't call my lawyer. Actually, I suppose you could try to call my lawyer, since I don't have one I don't think you will get too far (grin).