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 >= XP and on Unix platforms, including Linux and Mac OSX.
perl objects are used for various types of items 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 with 'private' parm files and point to them 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 test1.log and turn on all serial related debug messages:
mh -debug serial > test1.log
Gordon Meyer wrote a nice getting started article.
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 in Bruce's 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) item or Festival socket port
Setup the OLE Voice Recognition item or socket to the viavoice_server on linux
Setup the serial items
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 50 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 150 MHz PPro system.
There is lots of good perl info is at http://www.perl.com . The reference book is "Programming Perl, 3rd 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.
I fun way to slowly learn perl is with to get a perl FAQ per day. You can sign up here: http://perl.faq-by-day.org
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 item:
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 \.
The list of supported hardware interfaces is now maintained on the Misterhouse wiki at http://misterhouse.wikispaces.com/interfaces .
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. This can be a comma delimited set of paths if you want to keep your code files between different directories.
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/common/telnet.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.
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. You can also stack debug flags, using ; as a separator. fro example:
mh -debug serial;x10
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_Now, $Date_Now, $Date_Now_Speakable, $Year_Month_Now
$Time_Sunrise, $Time_Sunset, $Time_Sunrise_Twilight, $Time_Sunset_Twilight
$Second, $Minute, $Hour, $Mday, $Wday, $Day, $Month, $Year, $Holiday
$New_Msecond_100, $New_Msecond_250, $New_Msecond_500, $New_Second, $New_Minute,
$New_Hour, $New_Day, $New_Month, $New_Year
$Season, $Weekday, $Weekend, $Time_Of_Day, $Dark
%Moon
The complete list of global variables can be found on the Widgets menu of the default web page or at the top of /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 formatted time and date.
$Dark is true between $Time_Sunset_Twilight and $Time_Sunrise_Twilight, false otherwise.
The $New_* variables are true only on the pass that we enter a new Second, Minute, Hour, etc. $New_Msecond_100/250 are true every .1 and .25 seconds. There are also new_* functions if you want to test on every nth second/minute/hour (see new_second function).
$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.
$Keyboard echoes characters typed on the keyboard (currently windows only)
%Save is a hash in which the values of variables can be saved so that they remain unchanged after mh is re-started. The values can be checked using the web widgets menu.
%Misc is a hash in which other miscellaneous data can be stored. These values can also be checked using the web widgets menu. This is a handy place to put data you might want to display in .shml web pages.
These constants are defined, so we can use them without quoting them (e.g. if $state eq ON instead of if $state eq 'on'):
ON => 'on';
OFF => 'off';
DIM => 'dim';
BRIGHTEN=> 'brighten';
TOGGLE => 'toggle';
STATUS => 'status';
OPEN => 'open';
CLOSE => 'close';
OPENED => 'opened';
CLOSED => 'closed';
MOTION => 'motion';
STILL => 'still';
The Misterhouse documentation refers to perl objects as items. Most of these are derived from Generic_Item.
The old list of items is here. These will be moved to the new list soon.
The new list of items is here. This documentation is maintained in the .pm files along side the code.
The list of modules that are not items is here.
Checks to see if identified device is already in the chain of set_by devices. Basically identifies never ending loops of setting objects.
Returns:
If no_strip is set to true:
The original object which initiated this action, OR the same set_by object if it appears in the set_by list already.
If no_strip is set to false or undef:
Undef, unless would return something which is not an object, then it returns the text preceding a [, such as web, email, ...
Assigns a sound file to an event name. This allows us to create and review event sounds. See mh/code/common/event_sounds.pl for an example.
Example:
add_sound barcode_scan => 'sound_nature/bird.wav', volume => 20 if $Reload;
if ($state = state_now $barcode_scan) {
play 'barcode_scan';
}
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});
Here is a complete list of conversion functions:
convert_k2f # Convert degrees Kelvin to Farenheight
convert_c2f # Convert degrees Celsius to Farenheight
convert_f2c # Convert degrees fahrenheit to celsius
convert_f2k # Convert degrees fahrenheit to kelvin
convert_in2mb # Convert to inches of mercury to millibars
convert_in2mm # convert inches (per hour) to millimeters (per hour)
convert_km2mile # convert kilometers (per hour) to miles (per hour)
convert_mb2in # Convert millibars to inches of mercury
convert_mile2km # convert miles (per hour) to kilometers (per hour)
convert_mm2in # convert millimeters (per hour) to inches (per hour)
convert_mps2mph # convert meters per second to miles per hour
convert_mps2mph # convert miles per hour to meters per second
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});
Writes 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:
dbm_write($log_file, $log_key, $log_data);
logit_dbm($log_file, $log_key, $log_data);
dbm_write simply stores data as is: $dbm_file{$log_key}=$log_data
logit_dbm also stores an access count: $dbm_file{$log_key}="$access_count $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.
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.
Displays the specified text string, text file contents, or bitmap (.gif or .jpg).
Simple mode:
display $file_or_text, $time, $title, $font, $window_name, $append;
Options mode:
display option => 'value', text => $file_or_text;
Options:
$time is how long (in seconds) till the box will auto-close.
Defaults to 120. Use 0 to disable auto-close.
$title is the title of the display box.
$font is the font (default is mh.ini parm tk_font).
$window_name will re-use an exisiting display window of the same name.
$append can be top or bottom. Only meaningful with $window_name.
width and height overrides the auto-calculated values based on text
geomeotry places the window: +X+Y (e.g. +0+0 for upper left)
device Use this to pick a specific display. For example,
device=alpha will display data with the &display_alpha function.
app This uses the parms defined in the the mh.ini display_app parm.
This allows us to use custom parms with generic, common code.
Examples:
display "Internet message: $msg", 300, 'Internet message';
display $f_trivia_answer;
display '/pictures/photo.jpg';
display text => $log, time => 0, font => 'fixed', window_name => 'log';
display text => $text, time => 0, window_name => 'AIM', append => 'top';
display text => $f_top10_list, time => 300, font => 'Times 25 bold',
geometry => '+0+0', width => 72, height => 24;
display "device=alpha $caller";
display "app=bingo $bingo_text";
display app => 'bingo', text => $bingo_text;
If -tk 1 (i.e. Tk is installed and used), the data is displayed in a Tk popup window. If -tk 0 or the data is echoed to the console.
If the data is requested from a web browser, the data is echoed to the browser print_log window. It will be displayed to a local tk window only if the $time parm is specified.
Use this to run some code after some delay.
Usage:
eval_with_timer $code, $time;
Examples:
eval_with_timer 'print_log "hi from the past"', 60;
eval_with_timer '$Misc{insult}{flag} = 1', 2; # Sets flag after 2 seconds
If you want to do this with an item state, use the set_with_timer method.
Renames or optionally copies a file to the same name with the "backup1" extension. If the backup1 file already exists, it is renamed to backup2 first, backup2 is renamed to backup3, backup3 is renamed to backup4, and backup4 is deleted.
Examples:
file_backup $log_file, "force";
file_backup $parm_file, "copy";
Normally, this function will only run if the file is older than ten minutes. Use the "force" option if you want to override this behavior. Use the "copy" option if you want to copy the file instead of renaming it. The "copy" option implies "force".
Concatonates data from one file onto another. If an optional 3rd parm of 'top' is specified, the first files is added to the top of the 2nd file. If not, then it is added to the bottom.
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",
'top';
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_changed($file);
Checks to see if a file exists, and if not, returns a default file instead.
Usage:
file_default $file, $default;
Examples:
$f_insult = new File_Item(&check_default_file($config_parms{speak_insult_file},
"$config_parms{data_dir}/remarks/insults1.txt"));
Returns true if the contents of the 2 specified files are different.
Example:
print_log "Files are different" if file_diff($file1, $file2);
Returns the first few lines of a file
Example:
@data = file_head($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 size of a file, in bytes.
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);
Returns a member => full_path hash for all members in all dirs passed to it.
Examples:
my %file_paths = &file_read_dir(@Code_Dirs);
for my $member (keys %file_paths) {
next unless $member =~ /(\S+).menu$/i;
menu_parse file_read $file_paths{$member}, $1;
}
Returns the last few lines of a file
Example:
$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 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;
}
Writes data into a file. Like logit, except it writes over, rather than appends to, a file.
Examples:
file_write($file, $data);
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");
These functions can be used to manipulate mht 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:
address => 'ip_address' Use this to push wav files to
remote computers that are enabled to received
pushed wav files (e.g. Audreys). Configure
with the mh.ini parm voice_text_address_code.
Can be a comma delimited list of addresses.
rooms => 'room_names' You need to code a &Play_pre_add_hook to enable this.
See mh/code/bruce/pa_control for an example.
volume=> dd How loud (dd = 0->100).
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
nolog => 1 Does not log $file_name to the speech log.
app => 'xyz' This uses the parms defined in the xyz section of
the mh.ini speak_app parm. This allows us to use custom
parms with generic, common code.
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';
play address => '192.168.0.82,kitchen', file => '../sounds/hello_from_bruce.wav';
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. If you want the message logged to a specific file (in the directory pointed to by mh.ini parm data_dir), you can prefix the text with log=my_logfile. For example:
print_log 'log=test1.log This message is logged in test1.log';
print_msg is typically used for less frequent, more important messages. This is not currently shown on the default web windows.
print_speaklog is called by the speak and play functions, so should not be normally used in user code.
If you pass multiple arguments to these functions, they will print them with a space between, just as the perl print function does.
Use this function to round off a number. Usage:
round $number, $digits
If $digits < 10, $number is rounded to $digits to the right of the decimal
If $digits >= 10, $number is rounded to $digits nearest $digits
Examples:
$time_left = round $time_left, 1; Round to nearest tenth
$time_left = round $time_left, 2; Round to nearest hundredth
$rank = round $1, 100; # Round to nearest 100
$rank = round $1, 1000; # Round to nearest 1000
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.
If you want to track when the process finishes, use a Process_Item instead.
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 create events with built in delays without causing mh to pause
Example:
# After 2 seconds, this evals the print_log string
run_after_delay 2, "print_log 'Ending delay test 1'";
# This runs anonymous subs to print after 2 and 3 seconds.
run_after_delay 2, sub {
print_log "Printed after 2 seconds";
run_after_delay 1, sub {
print_log "Printed after 1 more second seconds";
}
}
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');
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])
SetFocus($window)
EnumChildWindows($window, \@childs)
WaitForAnyWindow will set the window handle for the window that matches the (sub)string specified in $title.
WaitForWindow is like WaitForWindow, but requires an exact string match for the window title.
SetWindowText is useful in changing a genericlly named window to a specific name that can then be accessed via WaitForAnyWindow. Also, since WaitForAnyWindow will match the first (left most) part of any title, GetWindowText allows one to get window specific info that can be used to determine the status of the window application.
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
SetFocus sets the focus to $window. It does not activate the window (the foreground application will not be changed if $windows belongs to another application).
EnumChildWindows enumerates all child windows that belong to $window and returns the handles in the @childsCODE> array. $window must be a valid window handle @childs must be an array reference.
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);
}
This function can be used to send the resulting text of a command or query to a specified target. By default, valid targets are:
display -> Text is passed to the display function.
email -> Text is passed to net_mail_send
im -> Text is passed to net_im_send
log -> Text is passed to print_log
speak -> Text is passed to speak
tk -> Text is passed to speak (or display if long)
web -> Text is passed to the web browser.
All parms given to respond are passed to the target function. So, for example, you can specify a specific email account using the to parm, like this:
respond target => 'email', to => 'joe@cops.com', text => 'Alarm just went off';
or as with the speak and display functions, you can specify your parms in-line, like this:
respond 'target=im to=joe@jabber.com pgm=jabber Alarm just went off';
The default respond target it whatever set_by is, so for example, if a command is run from an im client, text sent to any respond called by that command will be sent only to that im client. You can also specify multiple targets. For example:
respond 'target=speak,im,email app=notice The fire alarm just went off';
In this case, the app parm is only recognized by the speak function, and would be ignored by the im and email functions.
When called with no target, the default is to speak, or display if the text is long.
You can specify new targets, or override the default target reponse of the above default targets, by creating respond_xyz functions in your user code. So for example, if we had callerid code with this:
$caller = "Call from $caller. Call is from $caller.";
respond("app=phone target=callerid $caller");
You could add this function anywhere in your user code to change how it is spoken and to forward the call to your im client:
sub respond_netcallerid {
my (%parms) = @_;
$parms{text} =~ s/ ?\.[^\.]*$/\./; # Drop the extra 'call from'
&net_im_send(pgm => "AOL', text => $parms{text});
&speak("app=phone $parms{text}");
}
Passes specified text to the TTS program. There are 2 modes of calling it:
Simple mode:
speak 'text to speak';
speak 'option=value text to speak';
Options mode:
speak(option => 'value', text => 'text to speak');
Default options can be specified with mh.ini parms, using a
speak_ prefix. For example: speak_voice = mike
Options are:
address => 'ip_address' Use this to push TTS synthesized wav files to
remote computers that are enabled to received
pushed wav files (e.g. Audreys). Configure
with the mh.ini parm voice_text_address_code.
Can be a comma delimited list of addresses.
rooms => 'room_names' You need to code a &Speak_pre_add_hook to enable
this. See mh/code/bruce/pa_control for an example.
mode => 'unmuted' Forces speech, even in mute and offline mode
nolog => 1 Does not log the spoken text to the speech log.
app => 'xyz' This uses the speak parms defined in the xyz section of
the mh.ini speak_app parm. This allows us to use custom
speak parms with generic, common code.
These options are for the Unix Festival speech engine (but don't work yet):
rate => '+-nn%' (e.g. +10% or -30%)
voice => 'name'
These are for Unix IBM ViaVoice TTS engine (mh.ini sound_program=vv_tts):
default_volume xyz => default volume when -volume not set
volume xyz => volume setting for both play and voice unless specified
voice_volume xyz => voice volume setting
voice xyz => voice #
nomixer => do not use built in mixer support
play xyz => play's sound file xyz
These options are currently for the windows MS TTS engine only.
mode => any of the following
stop, pause, resume, rewind, fastforward, slow, normal, fast, dd
voice => 'name'
This can be any voice listed in the mh.ini voic_names parm or
any of the following:
random => randomly selects a voice
next => selects the next voice in the voice_names list
all => uses a different voice for each word. Note this was
inspired by dictionaraoke.org, but not all that useful.
Only works with xml enabled engines (MSV5 and NaturalVoice)
volume => dd (dd = 0 -> 100).
rate => fast,normal,slow,dd
For the V4 engine, dd is words per minute.
For the V5 engine, dd is -10 -> 10 for slowest to fastest
pitch => dd (dd = -10 -> 10)
card => d (d = 1,2,3...) Picks which sound card to used (MSV5 only)
d can also be a text string, if you use the mh.ini voice_text_cards option
to define which cards to enable (e.g. voice_text_cards = live,audigy).
To output to more than one card, specify a comma delimited list.
to_file => xyz. Saves speech to file xyz.
With the MS TTS V4 engine, the rate parm effects all subsequent
spoken text (volume and voice parms do nothing).
With the MS TTS V5 engine, the volume, rate, and voice parms will effect
only the specified text. If you want to change the default for all text,
specify the parm, but no text (e.g. speak voice => 'Sam')
The MS TTS V5 engine also supports embedded XML parms for other controls (e.g. pitch,
silence, pronounce, emphasis). See mh/doc/ms_speech_xml_example.* for examples.
You can use the mh.ini voice_names parm to correlate generic voice names to
specific names. For example:
voice_names = female=>Crystal, male=>Rich16, male1=>Rich16, male2=>Mike,
Rich=>Rich16, Mary=>Crystal, Sam=>Mike
Using voice=none will cause no text NOT to be spoken.
Examples:
$test_voice1 = new Voice_Cmd "Say something with at a volume of [50,100]";
$test_voice2 = new Voice_Cmd "Say something at a [fast,slow,normal] speed";
$test_voice3 = new Voice_Cmd "Say something in voice [male,female]";
speak(volume => $state,
text => "This is an example at a volume of $state") if $state = said $test_voice1;
speak(rate => $state,
text => "This is an example at a rate of $state") if $state = said $test_voice2;
speak(voice => $state,
text => "This is an example of a $state voice") if $state = said $test_voice3;
$Save{mode} = 'mute' if time_cron '0 10-21 * * 1-5';
speak 'mode=unmuted The front door just opened';
speak mode => 'pause';
speak mode => 'resume', rate => 120;
speak "address=bedroom,kitchen Wake up or die!";
speak "Now I am really, <pitch absmiddle='10'/>really excited!"
speak to_file => "$config_parms{data_dir}/test_tts.wav",
text => read_next $house_tagline if $New_Minute;
speak app => 'email', text => 'You have new email';
speak app => 'timer', text => 'The timer has expired';
More examples can be found in mh/code/common/test_speak.pl
Use this to add/delete an offset to a time value, using the same syntax as offsets allowed for in time_now.
Examples:
my $fishtank_light_on_time,$fishtank_light_off_time,$fishtank_light_duration;
if ($Startup) {
$fishtank_light_on_time = time_add "$Time_Sunrise_Twilight+3:00";
$fishtank_light_off_time = time_add "$Time_Sunset_Twilight+4:00";
$fishtank_light_duration = time_add "$fishtank_light_off_time-$fishtank_light_on_time";
}
This function returns the time difference between 2 time values. The roundoff time unit is dependent on how big the difference is between the 2 times. For example, if the time difference is > 5 days, the returned value will be in days.
Examples:
my $diff = time_diff $Moon{time_full}, $Time;
speak "The last full moon was $diff ago";
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
17: 2001-04-09 14:05:16 (POSIX strftime format)
18: 20011201 (i.e. YYYYMMDD)
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.
By default time_cron only operates on the pass when a new minute starts (i.e. $Second == 0). This can be overridden using a 2nd 'second' parameter to specify which second to trigger on. If this is set to '*', it returns true for all passes for the minutes that it matches on. This may be useful if a movement sensor is to trigger different events at different times.
Examples:
# Speak time every 15 minutes, between 7 am and 8:45 am on weekdays only
speak $Time_Now if time_cron '0,15,30,45 7,8 * * 1-5';
# Change PA speaker mode 30 seconds past at 9 pm, every day
pa_sleep_mode('kids', 1) if time_cron('* 21 * * * ', 30);
if (state_now $movement_sensor eq ON and !$Save{woken_up} and
time_cron('* 6-9 * * 1', '*') ) {
speak "It is Monday morning. Remember to put the rubbish out.";
$Save{woken_up} = 1;
}
These return true if the specified time is greater, less than, or equal to 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");
Returns true if the current time is between the 2 specified times.
If the end time happens to be earlier than the start_time, then it tests if the start-time is more than 12 hours in the future. If so, it subtracts 24 hours from the start-time, otherwise it adds 24 hours to the end-time.
Example:
speak 'Who goes there' if
state_now $motion_sensor and
time_between '10 pm', '6 am';
time_now is evaluate to true for the 1 pass that matches the specified date time. Date is optional. You can specify + or - offset, in hours:minutes:seconds. Time can include AM/PM or be in 24 hour format.
A optional 2nd 'second' parameter can be used to specify which $Second it returns true on (default is on a new minute, $Second == 0). If this is set to '*', it returns true for all passes for the minute that it matches on.
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 17:59', 45);
run('min', 'IR_cmd VCR,STOP') if time_now "$date $stop - 00:01";
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. Frequency is the average number of minutes between occurrences.
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;
}
print 'test value=2' if time_random '* * * * *', 2; # Fires every other minute;
print 'test value=10' if time_random '* * * * *', 10; # Fires every other 10th minute;
print 'test value=1' if time_random '* * * * *', 1; # Fires every minute (not useful)
Use time_random_offset to code a random time around a time_now formated time. It takes two arguments. The first is the same as the time_now function, '6:45 PM' for example. The second is an offset in minutes (60), seconds (:45), or minutes and seconds (2:30). The function returns true at some random time between the time specified and the offset.
The offset can only be positive and if you restart between the time specified and the random offset, it won't fire.
Usage:
time_random_offset($time_now_spec, $offset)
Examples:
speak "random test" if &time_random_offset('1:20 pm', '1:23');
set $light ON if &time_random_offset(&time_add("$Time_Sunset + :15"), 15));
This function will add or edit Misterhouse parameters in the user's ini file. It will make a backup of the ini file, and it doesn't remove any comments in the file. The first argument is a hash of parameters to set. The second (optional) argument is the ini file you want to modify, and the third (optional) argument is set to 1 if you want to log the change.
Examples:
write_mh_opts({"photo_dir" -> $state}, undef, 1);
write_mh_opts(%parms);
These functions are like the $New_Second/Minute/Hour variables, except they can return true only on the nth second/minute/hour.
Examples:
print_log "This occurs every 10th second" if new_second 10;
print_log "This occurs once every even hour" if new_second 2;
If you pass no argument, it defaults to 1 (i.e. the same as the $New_* variables).
The tk widget functions are documented here.
The trigger functions are documented here.
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.
These functions 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 an 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').
If you call net_domain_name_start, then check for net_domain_name_done, it will query the DNS servers as a background task, so mh will not pause if the DNS server response takes a while. The 'search_name' string must be unique for that part of the code, so that the net_domain_name_done test gets the correct results.
The data is cached. If the domain has already been searched, net_domain_name_start will return the same results as net_domain_name_done.
Examples:
# Run in the foreground
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';
# Results: $name = 'www.ibm.com', $name_short = 'ibm';
# Run in the background
$v_test_dns2 = new Voice_Cmd 'Run the dns test2';
$v_test_dns2 -> tie_event("net_domain_name_start 'test', '204.146.18.33'");
print_log "Domain=$state" if $state = net_domain_name 'test';
Checks to see if an IP address is available. Returns true if pingable.
Example:
&net_ping($host); # Default protocol is specified in mh.ini
&net_ping($host, $protocol);
Note this may cause my to hang for a while if the target is not pingable. A better way of testing ping-ability to use a Process_Item call. An example is in code/common/internet_connect_check.pl
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)
passive Set to 1 to get a passive FTP (sometimes needed for firewalls)
Example:
net_ftp(file => 'index.html', command => 'put', passive => 1);
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.
Returns true if the specified host:port is avilable, 0 otherwise.
Usage:
net_socket_check($host_port, $protocol);
Examples:
return &net_socket_check("$host:$port");
Used to send and AOL Instant Message, Jabber, or MSN 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.jabbercentral.org.
Usage:
net_im_send(option => value);
These are the options:
pgm Default aol. Can also be jabber or msn
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 a server. Subsequent messages use the same signon, so are sent much faster.
Once you have been logged in, mh will also display incoming messages. This code has lots of other possibilites that can be added, since jabber is XML based and very flexable.
Example:
net_im_send(pgm => 'jabber', 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';
More examples are in mh/code/common/internet_im.pl
Used to send email. To run commands via email, see FAQ question 2.12: Can do I send mh comands via email?
There is also a stand alone bin/send_email command you can use if you find &net_mail_send causes mh to pause.
Usage:
net_mail_send(option => value);
These are the options:
server Default is mh.ini parm net_mail_ACCOUNT_server or
parm net_mail_ACCOUNT_server_send
port Default is 25 or parm net_mail_ACCOUNT_server_send_port
from Default is mh.ini parm net_mail_ACCOUNT_address
to Default is mh.ini parm net_mail_ACCOUNT_address
This can be a comma or semicolon delimited list of addresses
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 or a file whose
contents you want attached to the note.
Note: You can not use file and text at the same time, as they will be combined.
filename Name to give file attachement. Defaults to file parm.
mime Set to the mime type (used if a file is attached).
Current recognized types are txt,pl,zip,bin,exe,jpg,gif,png, and html.
If not specified, the extention of the file parm is used.
Use bin for an arbitary binary file.
Can also be html_inline if you want the html inline, rather than attached.
baseref If sending html with mime => 'html', use this to set the BASE HREF
priority Can be 1->5 (1 is high, 5 is low). Default is 3.
Example:
net_mail_send(text => "Test email sent at $Time_Now\n\n");
net_mail_send(account => 'Bruce', to => 'winter@misterhouse.net', text => $msg);
net_mail_send to => 'winter@misterhouse.net,santa@claus.net', text => 'hiho';
net_mail_send(subject => 'test an html file attachment',
baseref => 'localhost:8080',
file => '../web/mh4/widgets.html', mime => 'html');
net_mail_send(subject => 'test a gif file attachement',
file => '../web/graphics/goofy.gif');
# Use run to launch a background process
run 'send_email -subject "test" -text "Test background send_email"';
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
port Default is 110 or parm net_mail_ACCOUNT_server_port
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/common/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.
Deletes mail from an account. A dangerous, but requested, function.
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 xyz => name of recipient (must be defined in mh.ini)
-pin xyz => pin number
-message xyz => 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'
backup_data walks directories and stores selected files to gziped tar files.
Usage:
backup_data [options] dir1 dir2 etc
-h => This help text
-file xyz => Name of tar file. Default is backup
-size xyz => Do NOT store files > xyz KBytes. Default is 100
-skip xyz => Skip any file or dir that matches regex xyz
-age xyz => Only back up files changed in the last xyz days
-no_zip => Do NOT gzip the tar file.
-no_zip => Do NOT suffix the files with date stamp.
-int => Use the internal perl tar and gzip modules, rather
then external tar and gzip programs. This is slower
and users more memory (> the data being tared).
Examples: backup_data /www backup_data -size 10 -no_zip -int /usr/local/bin backup_data -file /backup/misc /bin //misterhouse.net/bin backup_data -file /backup/mh -skip "(/tv$)|(/articles$)" /misterhouse backup_data -file /backup/mh_articles -size 100000 /misterhouse/articles backup_data -file /backup/docs -age 30 -size 100000 /docs
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 xyz to have the window auto-close in xyz seconds. Set to 0 for no auto-close.
-font xyz to pick the font
-title xyz 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)
find_files finds files on shared networked Windows and samba directories
Usage:
find_files [options] search_string
-h or -help => This help text
-v => Verbose (shows dirs as they are searched)
-boxes x,y,z => Search for files on boxes x,y, and z. Defaults to all.
-dirs x,y,z => Search only dirs x,y, and z. Defaults to all.
-skip x,y,z => Ignore files/dirs with strings x,y, and z.
Examples: find_files mp3 find_files -v -boxes "house,dm,c2" mp3 find_files -dirs "//house/c,//dm/d" mp3
find_programs finds programs running on networked Windows computers.
Requires WMI to be installed on both/all computers.
Usage:
find_programs [options] box_list [search_string]
box_list is a comma delimited list of boxes to search
search_string lists only programs that match
options:
-h or -help => This help text
-all => Show/search all programs
Examples: find_programs house find_programs "house,dm,c2" quake find_programs -all "house,dm,c2"
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.
mh/data/email
This directory will have one weeks worth of html mail files,
by account and day (e.g. winter_tue.html), as well as a
latest.html file that just shows the unreceived mail.
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.
Use the mh.ini net_mail_scan_age parm to set the age, in minutes, of email to report/scan. If blank, (default), get_email reports on all mail on your server. If your have your mail client set to 'leave mail on server' (useful if you want to use appliances like Audrey to also read mail), you will probably want to set this to something like 15.
Here is an example of how to call it from mh (see mh/code/common/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/bruce/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. Creates a DBM for use by get_tv_info.
Usage:
get_tv_grid [options]
-h => This help text
-help => This help text
-userid xyz => xyz is your clicktv userid. Go to http://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 retrieved 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 "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.
-keep_old => Do NOT delete data from the DBM that is one month older
than todays date
-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
Example:
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:8090
Usage:
get_tv_grid [options]
Options:
-h => This help text
-help => This help text
-zip xyz => xyz is your zip code
-provider xyz => xyz is your TV provider ID. See note below
-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:8090
Note on finding your provider ID:
Enter your zip code at http://tvlistings2.zap2it.com/
View the html source and pick the number from value='nnnnnn'
by doing a string search for you provider. For example:
<OPTION value="255248">Charter Communications - Rochester</OPTION>
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.
Version: 1.14
Usage:
get_tv_info [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).
-times 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.
-early_am xyz => Adds info for shows after midnight.
Formats are same as for to times.
-increment xyz => Time increment is xyz minutes (5 or 30). Default is 5.
-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.
Note: xyz can be a regular expression (e.g. -keys "^ER$")
-keyfile xyz => List of keys to search for, from a file in the data directory
All of the above parms support these formats:
: comma-separated 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
-quiet => turn off normal errata
Examples: get_tv_info -channels "4-12" -lengths 2 get_tv_info -channels "4,6,12" -times "7 pm" get_tv_info -dates "7/4-7/11" -keys "star trek, er ,dilbert" get_tv_info -dates +14 -keys computer get_tv_info -time "17-23"
Shows which have already started are excluded unless you search
without specifying -times or you search for -times 'all'.
get_url gets a web page and echoes 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.
Options are:
-quiet: no output on stdout
-cookies 'cookiestr': Uses the specified cookie string for the request.
The format of 'cookiestr' should be like this: 'name1=val1; name2=val2'.
-cookie_file_in 'file': Like the -cookies option, but string is sent via a file.
-cookie_file_out 'file': Generates a cookies string from the resulting web request.
-userid 'userid' : Use these 2 parms if your web page is protected by
-password 'password': 'basic credentials' (i.e. you get a browser popup).
-post 'poststr': Makes this a post request with the specified name/value pairs
as the form data, such as: 'name1=val1&name2=val2'
-header 'header_file': HTTP headers from the server are stored in this file
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 xyz => xyz is the City you want.
-state xyz => xyz is the State you want.
-data xyz => xyz is either conditions, forecast, or all. Default is all.
-refresh xyz => xyz 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 echoes 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 XA1AJ
The last example will fire trigger an X10 command, but only if you have an item defined that matches that data (e.g. X10 A1 in the above example).
ical_load reads an icalendar calendar file from the Unix ical program. Optionally creates a mh code file to implement calendar events.
See outlook_read for a Windows solution.
Version: 1.0
Usage:
ical_load [options]
Options:
-help -> help text
-calendar_file xyz-> Location of .calendar file. It defaults to $HOME/.calendar.
-quiet -> do not echo data to STDOUT
-debug -> print out debug
-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 mm/dd/yy -> Get data with a start_time on date. Default is today.
-days ### -> Look out ### days from -date. Default is none, today only.
Examples: ical_load -help ical_load -calendar_file /home/dbl/.calendar -pl_file /projects/mhcode/calendar_events.pl ical_load -calendar_file /home/dbl/.calendar -date 11/07/00 -days 2 ical_load -calendar_file /home/dbl/mh/data/calendar ical_load
A Unix script 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.
On windows, this function is built into the mh.bat script.
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)
passive 0/1. Default is 0
timeout Defaults to 20 (seconds)
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.
See ical_load for a Unix solution.
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/common/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 xyz -> Files the data into mh/data/mhsend/xyz
-log xyz -> Logs the data into mh/data/mhsend/xyz.log
-run -> Runs the data as command.
-display xyz -> Displays the data. xyz is how log to leave the display up.
-speak -> Speaks and displays the data.
-pwfile xyz -> 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 various menus (e.g. web, telnet, wap) will prompt for it. By default, the mh setup web pages can only be controled with the admin logon, but you can add/delete other commands by adding authority=admin to the password_allow file. You can also specify authority with the set_authority item method, or by adding it to your web/bin xyz.pl file # Authority=user comment.
The crypt function limits the password to 8 characters. If -user is not specified, it defaults to family.
Usage:
set_password [options]
Options:
-h => This help text
-help => This help text
-check => Will check, not set, the password. Turned on if -pw_file does not exist.
-user xyz => xyz is the user to set the password for.
-password xyz => xyz is the password to check.
If not used, a TK popup window will prompt for it.
-pw_file xyz => xyz is the file that the crypt-ed password is read/written to.
Default is mh/data/.password
Examples:
set_password -user admin
set_password -user family -password xyz
speak will pass the file or text to mh. It simply runs the house program.
Example:
speak "Boo, did I scare you?"
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 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 nested create menus that let you query mh data or control mh items or commands. You can use any or all of these interfaces to walk through menus:
Web browser: http://localhost/sub?menu_html
WAP phone: http://localhost/sub?menu_wml
Tellme.com phone: http://localhost/sub?menu_vxml
LCD keypads: see mh/code/bruce/lcdproc.pl for an example
Audible feedback: see mh/code/public/audible_menu.* for an example
To enable, use mh/code/test/menu.pl to read in menu files. The format of the menu files is a simple text file, where each record is prefixed with a record type character. Here is an example:
M: Test # Top level menu can have any name
D: HVAC
D: Lights
M: HVAC
D: Indoor temperature
R: At $Time_Date, the temperature is $Weather{TempIndoor}
D: Living fan [on,off]
A: set $living_fan $state
D: Outdoor temperature
A: What is the outdoor temperature
R: last_response
D: Humidity
A: What is the humidity temperature
M: Lights
D: Camera [+,-,^,v]
A: Camera light [on,off,+30,-30]
R: eval "Light $state" . chr(07) # Ring bell
D: Bedroom [on,off,+30,-30]
P: anyone
M: Outside
M: Outside
P: anyone
D: Garage Light [on,off,+30,-30]
D: Backyard Light [on,off,+30,-30]
These are the record types:
M: Menu screen
D: text to Display as a menu choice
P: Password bypass group
A: what Action to run (either a voice cmd or code to eval)
R: what Response to display.
last_response will pick up the last mh response
no_response will return no response
href=url will goto the url (is using a web browser)
If not specified, defaults to select state or last_response.
Begin with eval to eval the string first.
The above example specifies 3 menu (M:) records that creates a Test menu, with 2 submenus, HVAC and Lights.
The HVAC 'Indoor temperature' display record (D:) will respond (R:) with the indoor $Weather data.
The 'Living fan' entry will evaluate the 'set $living_fan $state' action (A:) record to set the fan to the specified (on or off) state.
The 'Outdoor temperature' record will run a Voice_Cmd action (A:), and respond with the last data printed or spoken by mh (the results of the outdoor temperature command). The 'Humidity' record will also run a Voice_Cmd and default to last_response.
The 'Camera light' record also runs a Voice_Cmd action. To save (LCD) display space, the on,off,+30,-30 Action states were renamed on the Display record to +,-,^,v.
The 'Bedroom' record matches a Voice_Cmd exactly, so no action record is required. It specifies a Password bypass group of anyone, meaning anyone can run this command without having to use a password.
The last Outside Light records are a 3rd level menu, nested under the 2nd level Lights menu. There are no limits to how deep you can nest menus and they can appear in any order you want in the file. The same menu can be a submenu of more than one other parent menu.
The Outside menu specifies a P: record before any items are specified, so that is the default for all items on this menu, meaning anyone can run the commands on this page without a password.
If you want a starting point to customize your own menus, you can find an auto-generated menu for all the mh Voice_Cmds in the mh_temp.menu file in your code directory. This file is re-generated on reload, so copy and edit this file, then point menu.pl at your edited copy.
You can have different sets of menus, for use with different interfaces or users. For example, the default menu.pl looks like this:
my $menu_mh = menu_create "$config_parms{code_dir}/mh_temp.menu";
my $menu_test = file_read "$config_parms{code_dir}/test.menu";
menu_parse $menu_test, 'default';
menu_parse $menu_mh, 'mh'; # This menu has all the mh voice commands
menu_create creates the example mh_temp.menu file for all voice commands and returns it into $menu_mh. $menu_test has the test menu, similar to the above example. You can specify the menu group (e.g. default or mh) when calling any of the menu interface subroutines. For the above menu.pl, you can point your WAP phone to:
Either of these will get the 'default' test menu:
http://misterhouse.net:8090/sub?menu_wml
http://misterhouse.net:8090/sub?menu_wml(default)
This will get the auto-generated 'mh' entry for all voice commands:
http://misterhouse.net:8090/sub?menu_wml(mh)
Something like this could be used to create different menus for different LCD controllers:
$menu_standard = file_read 'standard.menu';
$menu_bedroom = file_read 'bedroom.menu';
$menu_living = file_read 'living.menu';
$menu_all = file_read 'all.menu';
if ($Reread) {
menu_parse $menu_bedroom $menu_bedroom . $menu_standard, 'bedroom';
menu_parse $menu_living $menu_living . $menu_standard, 'living';
menu_parse $menu_all $menu_all . $menu_living .
$menu_bedroom . $menu_standard, 'all';
$lcd_data{bed1} {menu_group} = 'bedroom';
$lcd_data{living1}{menu_group} = 'living';
$lcd_data{living2}{menu_group} = 'all';
}
To test your menus, point your browser to http://localhost:8080/sub?menu_html To view auto-generated menus, try http://localhost:8080/sub?menu_html(mh)
If you have a LCD supported by lcdproc program ( http://lcdproc.omnipotent.net ) (e.g. crystalfontz or matrix-orbital), you can use mh/code/bruce/lcdproc.pl to display menus. Currently only the linux version of lcdproc supports the keypad option on the maxrix-orbital displays. Several other mh'ers are writing similar code for their LCD displays.
If you have a 24x7 internet connection, you can walk the menus with either a WAP compatible cell phone, or using voice commands with any phone via a Voice Portal like http://studio.tellme.com.
For cell phones, simply point your phone to http://your_domain/sub?menu_wml. You can test your menus with a web based phone simulator at http://www.yospace.com or download a phone simulator from http://phone.com . Other browsers are listed at http://www.palowireless.com/wap/browsers.asp , although I didn't have much luck with http://www.wapsilon.com or /http://www.gelon.net .
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_xyz 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_xyz 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_xyz 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_xyz subroutines in your own code for more flexibility.
For more info on the tk_xyz widgets, see look in the mh functions section of this document.
If you want to just update parts of the default web interface, use this mh.ini parm to point to your own dir:
html_alias2_ia5 = /misterhouse/web/ia5
Then override just the members from the default mh/web/ia5 directories that you want to change. For example, to override the security menu, copy mh/web/ia5/security/menu.html to /misterhouse/web/ia5/security and modify. The other files will still be picked up from the default distro dirs.
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.
If you want to enable MsAgent support, use the mh.ini html_msagent_script* parm. MsAgent allows remote IE web browsers to do TTS and VR on the remote box. Note, you can do this even if you are running mh on a linux box as long as your remote box is running IE. To enable, add the new mh.ini html_msagent_script* parms that points to a mh/web file with the agent code (currently only Jeff's miniJeff agent is available). Then click on the MSAgent checkbox at the top of the Web menu to turn him on or off.
IE, by default, will run each command only once, unless you change this setting: Tools, Internet Options, General, Settings button on Temporary Internet Files, under "Check for new versions of stored pages" select the option "Every visit to the page."
The web Items and Groups displays will use icons to indicate the item state. Icons will be searched for in the mh/graphics directory according to the item icon method, the item name, the item state, and the item 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 items using the icon method, item 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 compatible 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 , http://www.rad.kumc.edu/icons/icons.htm and http://www.engineeringplastics.com/HomeSeerIcons/
Important note: If you do play with adding/deleting/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 dynamically 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_name
This can be used to list just the commands in the specified Category.
/list;h_response?category_name
As above, but it also passes h_response to the RUN commands (see h_response below)
/groups
This will return all the Group Items
/list?group=$group_name
This will return all the items in $group_name
/items
This will return all the non Voice items (e.g. X10_Items)
/list?item_type
This will return all the items of type item_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 recent print_log text
/error_log
This lists the most recent error_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 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/bin/*.pl files.
If you want to run with a classic cgi that return html so STDOUT, specify #!/local/bin/perl on the first line of your file. An example is in mh/web/bin/test_cgi.pl
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. To pass arguments to your .pl file, append them to the URL in normal CGI fashion after a ?. For example:
http://misterhouse.net/test/test1.pl?argument1&argument2
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 extension 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"-->
or
Missing page: <!--#include var="$Misc{missing_url}"-->
mh global variables (including %Save and %Misc values) are accessible to .shtml pages. To make other variables accessible to .shtml pages you need to declare them with use vars '$variable_name'.
If you use svar=, instead of var=, the var contents will be returned only if the web user is authorized (i.e. logged in or from an ip address listed in password_allow_clients).
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 or as a h_response to a /SET or /RUN command (see below). 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 {
my ($arg1, $arg2) = @_;
return "results from function 2: $arg1, $arg2";
}
# Add this if you want unauthorized web access to these functions
if ($Reload) {
$Password_Allow{'&web_func1'} = 1;
$Password_Allow{'&web_func2'} = 1;
}
Then call them like this:
<p>Test web functions
<!--#include code="&web_func1"-->
<!--#include code="&web_func2(myarg1,myarg2)"-->
Or with this:
http://localhost:8080/SET;&web_func2(myarg1,myarg2)
One example of a built in function is html_item, which will return html for one item. For example:
<!--#include code="&html_item('v_what_speed')"-->
<!--#include code="&html_item('mode_mh')"-->
Another 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. An optional 4th parameter is a regular expresion that can be used to subset to matching files. See mh/web/graphics/index.shtml and mh/data/email/index.shtml for examples.
If you want to have only a few specific html controls, you can write your own html using the RUN and SET (or 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?$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 (or SET_VAR ... they are now the same) command can set any item or variable defined in your user code. The RUN command can run any voice command. Use '_' to replace blanks in the command string.
SET, RUN, or SUB can use an optional h_response field to decide what html will be returned:
Format:
SET;h_response?$item?value
RUN;h_response?command
SUB;h_response?sub_name(arg1,arg2)
$var and $item are the variable and item names, with or without the $ prefix.
h_response is optional and can be one of the following:
&function The results of &function will be returned
&function(args) The results of &function(arg) will be returned
no_response Tells the browser to do nothing (page is not changed).
last_response The last displayed or spoken text.
last_response_## As above, but only the first ## characters.
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
referer The browser will be re-directed back to the calling page
referer/URL The browser will be re-directed to the referer_root/URL
&referer(URL) The browser will be re-directed to the referer_root/URL.
http://url The browser will be re-directed to URL
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 results
form the user defined function &my_response (Note, to give anyone access to this
function you will need to authorize &my_response with this in your code:
$Password_Allow{'&my_response'} = 1):
HTML:
<form action="SET;&my_response">Test 2<input size=9 name="$test_form"></form>
User code:
$test_form = new Generic_Item;
print_log $state if $state = state_now $test_form;
sub my_response {
return "You entered " . state $test_form;
}
This will set a $barcode_scan Generic_Item, then display a web page
that was updated by user code:
<FORM ACTION="SET;barcode_search.html" target='speech'>
<p>Scan data into here: <INPUT SIZE=60 NAME="$barcode_scan" value=""></FORM>
This will redirect the browser back to the calling page
(note the $ from the $camera_light item is optional):
<a HREF="http://misterhouse.net:8090/SET;Referer?camera_light=on">Light on</a>
This will redirect the browser to mh page ia5/lights/main.shtml page:
<a HREF="http://misterhouse.net:8090/SET;referer/ia5/lights/main.shtml?$camera_light=on">Light on</a>
This will redirect the browser to misterhouse.net
http://localhost:8080/RUN;http://misterhouse.net?test_volume_at_60
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>
Here is an example of using a subroutine to generate html:
http://misterhouse.net:8090/SUB;tellme_menu(,,html)
You can also use a separate web server (e.g. Apache or ISS) to serve pages that control MisterHouse. For example:
Turn lamp
<a href="http://localhost:8080/SET;referer?$light1?on">on</a>
<a href="http://localhost:8080/SET;referer?$light1?off">off</a>
To enable on-the-fly icons to that show current item states, add this function to your code:
sub web_icon_state {
my ($item) = @_;
my $obj = &get_object_by_name($item);
my $state = $obj->state;
my $icon = "$main::config_parms{html_dir}/graphics/$state.gif";
print "db icon=$icon s=$state i=$item\n";
my $image = file_read $icon;
return $image;
}
Then add this to your html:
<img src=http://localhost:8081/sub?web_icon_state('$light1')>
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 ViaVoice on linux and how to enable different TTS engines with Festival.
There is a now also a Festival lite ending that looks promising at http://www.speech.cs.cmu.edu/flite/
For vr with ViaVoice, use mh/code/bruce/viavoice_control.pl
There are several reasons you might want to run one or more extra proxy versions of MisterHouse:
- Allows the real real mh to not pause while reading or
writing to slow interfaces (e.g. X10 CM11, CM17, or iButton).
- You can monitor and control remote serial ports. For
example, an old PC running MisterHouse in a barn,
or an internet connected PC in a vacation home.
- Sharing of interfaces between different mh boxes.
For example, you can run a test version of MisterHouse
to debug new code without buying a 2nd interface.
- Use small, quiet, low power PC's in differnet rooms to
distribute speech from your main MisterHouse with
the speak rooms= parm, using eithernet instead of a
relay / PA wiring scheme.
- Wean yourself off of one OS onto another, sharing ports
from your old computer to the new one.
To run a proxy mh, copy and modify the mh/bin/mh_proxy.ini and mh_proxy (unix) or mh_proxy.bat (windows) files. Then run mh_proxy. If you have tk installed, you may want to run mh_proxy -tk 1 while debugging.
Another option, instead of running a simple pruned down mh with mh_proxy, you can also simply run a 2nd normal version of mh, and simply include the proxy code dir in you mh.ini code_dir parm and add the server_proxy_port parm. For example:
code_dir = c:/misterhouse/my_code,c:/misterhouse/mh/code/proxy
server_proxy_port = 8085 # Used by the proxy_server.pl
On your main mh box, change your mh.ini port parms to point to the address:port of the proxy box, like this:
cm11_port = proxy localhost:8085
cm17_port = proxy localhost:8085
iButton_serial_port = proxy localhost:8085
You can start or restart either the main mh or mh_proxy before or after the other one and they should sync up. Tested interfaces include cm11, cm17, iButton, weeder, mr26, wx200 weather, modem, netcallerid, and ham tnc.
If you want to put an iButton port on a proxy, you will need to also run a code file on the proxy that defines all the iButtons you will use on that port. These iButton item names must match those on in your real code on your main mh box. One way to do this is to share the same iButton.pl code file on your proxy as you use on your main mh box, using the mh.ini/mh.proxy.ini code_dir and load_code parms. For example:
main mh:
code_dir = c:/misterhouse/bruce
proxy mh:
code_dir = $Pgm_Root/code/proxy,c:/misterhouse/bruce
only_load = proxy_server.pl,ibutton.pl
If you want to distribute speech to mh_proxy machines, see mh/code/public/speak_proxy.pl for example code.
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 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
By default, declaration code like '$item = new ...', or 'my $var = ...' is pulled out of the user loop code and put in the startup code, so it is only run once. If you want to force additional code out of the loop code, you can use add # noloop=start/stop comments before and after the code segment, or you can add a # noloop comment to the end of the record. For example:
# Example of noloop record comments
my $weather_wind_gust_threshold=$config_parms{weather_wind_gust_threshold}; # noloop
$weather_wind_gust_threshold=12 unless $weather_wind_gust_threshold; # noloop
# Example of noloop block comments
# 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 2nd argument to 'persistent' (a value of 1 will also work ... grandfathered in for old code). For example:
&main::MainLoop_post_drop_hook( \&jabber::process, 'persistent' );
If you want that hook to run first, before all other hooks of that type, set the 2nd argument to 'first', or 'persisent_first'. Otherwise the hook is pushed to the end of the list, so it is run in the order the hooks were added.
Here is a list of all the code hook locations:
MainLoop_pre => Run before user code
MainLoop_post => Run before user code
Serial_data => Run on any incoming serial data
Serial_match => Run when incoming serial data matches and item
State_change => Run when items change states
Play_parms => Run before playing wav files, so you can set play parms
Play_pre => Run before playing wav files
Play_post => Run after playing wav files
Speak_parms => Run before speaking text files, so you can set speak parms
Speak_pre => Run before speaking text files
Speak_post => Run after speaking text files
Log => Run on all print_log, display, speak, and play calls.
Reload_pre => Run before reload
Reload_post => Run after reload
Exit => Run on exit
If you want to use a hook to modify parameters for the subsequent mh function, use a _parm hook, which passes parms in by reference rather than by value. For example, this will add a to_file option to all speak calls:
&Speak_parms_add_hook(\&speak_parm_update) if $Reload;
sub speak_parm_update {
my ($parms_ref) = @_;
$$parms_ref{to_file} = "somename.wav";
}
To monitor text sent to print_log, display, play, and speak, try this:
&Log_add_hook(\&my_log) if $Reload;
sub my_log {
my ($type) = shift @_;
my ($text) = shift @_;
my (%parms) = @_;
print "type=$type text=$text\n";
}
For more examples on code hooks, see mh/code/examples/test_code_hooks.pl
The JDS and Marrick interface modules are untested.
mh also has 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 '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 windows. 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).
Adding, deleting, or changing Voice commands does is not recognized by the Windows MSVoice application 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.
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.
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
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
mh can be download from http://misterhouse.net
Here is a list of articles written about MisterHouse:
http://misterhouse.net/articles.html
You can subscribe and/or view the mailing list at https://lists.sourceforge.net/lists/listinfo/misterhouse-users You can view the archive at https://sourceforge.net/p/misterhouse/mailman/misterhouse-users/ and search it at https://sourceforge.net/search/?group_id=1365&type_of_search=mlists you can also view and search the mailing list archive at: http://misterhouse.10964.n7.nabble.com/
You can post to the mailing list using nabble as well, however you will need to subscribe to the mailing list first, before any messages from nabble will be accepted on the list.
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/HomeNetwork/
Copyright (C) 1998-2005 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).