NAME

mh - MisterHouse Home Automation Program


SYNOPSIS

mh is a perl program for time, event, web, socket, and voice based home control functions. It currently runs on Windows 95/98/NT and on Unix platforms, including Linux and Mac OSX.

perl objects are used for various types of objects to give a powerful but concise programming interface. Here is some example code:

   $dishwasher = new X10_Item 'B1';         # B1 is the dishwasher X10 code
   set $dishwasher ON if time_now '10:30 PM';
   speak "I am now closing the curtains at $Time_Now" if time_now "$Time_Sunset + 0:15";
   speak "The time is $Time_Now" if time_cron '30,35,40,45,50 6 * * 1-5';
   play(file => 'stairs_creek*.wav') if state_now $movement_sensor eq ON;
   $v_bedroom_curtain = new Voice_Cmd '[open,close] the bedroom curtains';
   curtain_on('bedroom', $state) if $state = said $v_bedroom_curtain;

Usage Options

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.

Usage Examples

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

Installation

The system requirements and installation instructions are detailed in mh/docs/install.html

There is also a FAQ that covers misc. topics in mh/docs/faq.html

We have a wiki at http://misterhouse.wikispaces.com for user-supplied documentation.

Gordon Meyer wrote a nice getting started article.


DESCRIPTION

mh overview

Items and events are defined by using your favorite text editor and creating members of perl code in a directory of your choice. For example, all the code that runs my house is in the /mh/code/bruce directory.

After starting mh, in addition to the timed, serial, or X10 based events that you defined, you can trigger mh actions with any of the following:

  - Voice commands.  On Windows platforms, you can use the MSVOICE icon in your tray
    (the green V) to control the Voice Recognition (VR) mode (manual,
    keyword-activated, or continuous). You also select your TTS (Text To Speech)
    voice with MSVOICE.
    On Unix platforms you can use the Festival TTS engine and pick among different
    accents and languages.  On Linux, you can install IBM's ViaVoice SDK
    to enable Voice Recognition.
  - A web browser pointed to http://localhost:8080 .  In addition to several
    example web interfaces, if you are familiar with HTML, you can write your
    own customized interface.
  - Typing any voice command in a telnet window.  This is customizable
    with the telnet.pl module.
  - Typing any voice command in the command box in the mh tk gui window.
  - Typing any voice command from a dos box, using the mh/bin/house command
  - Having any program write voice commands to the 'xcmd_file' (see mh.ini)
  - Triggering on user defined Socket or Serial port events.

Here are the basic steps of the mh program:

   1: Read config files and initialize things
       Setup the OLE Text To Speech (TTS) object or Festival socket port
       Setup the OLE Voice Recognition object or socket to the viavoice_server on linux
       Setup the serial objects
       Setup the tcp socket ports
   2: Read and evaluate/compile the user event code into the mh_temp.user_code file:
       Optional *.mht files are processed by mh/lib/read_table_*.pl, creating *.mhp files
       Objects are evaluated and created.
       Everything else is put into a &user_loop function (at the end of mh_temp.user_code file).
   3: Loop until exit, each pass executing:
       Set global time/date variables
       Check for voice commands
       Read/write socket data
       Read/write serial data
       Check for timers actions
       Check for external command files
       Evaluate the &user_loop function
       Sleep for a while (default is 100 milliseconds), so mh does not hog the CPU.

The interpreted nature of perl allows us to change/add/delete an event member quickly, without re-starting mh. After changing code, you tell mh to re-load the user event code (step 2 above). If there is a syntax error with the new code, the previous version of the code is re-loaded.

Re-loading 20 event members with 1500 lines of event code takes about 2 seconds. With a 50 millisecond sleep time, mh takes about 2->5% of the CPU and loops about 18 times per second on our 150 MHz PPro system.

Perl Info

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

For those of you using the compiled, instead of the interpreted, version of mh, the standard perl documentation can be found at http://www.perl.com/pub/v/documentation , http://language.perl.com/CPAN/doc/manual/html/index.html and/or http://www.activestate.com/ActivePerl/docs

Perl can be a pretty intimidating language to learn, due to its many options and functions, but for the purposes of using it to code mh events, there are only a few simple rules to keep in mind. Here are a few of them:

   Variables start with a $
   Strings are quoted with ' or ". Use " if you want variables within
   the string to be substituted.
   Commands end with ;
   Comments start with #
   You can implement block comments with =begin, =cut records, as follows:
      print "this will run\n";
      =begin
      print "this";
      print " will not run\n";
      =cut
      print "all done\n";
   Do loops start and end with { }
   If statements can be done either of the following ways:
      if (test) {action}
      action if test;
   If tests are == (or !=) for numeric data and eq (or ne) for string data.
   Object methods can be specified 2 ways.
   Here are both examples on how use the method 'set' for an X10 object:
      set $dishwasher ON;    # Indirect object form
      $dishwasher->set(ON);  # Classic 'object oriented' form
   Regular expressions are used to parse strings.  They are very powerful
   and can be quite complex, but here are a few simple rules:
      They are triggered with the =~ operator
      By default, they are delimited with //
      .  stands for any character (including blanks)
      \S stands for non blank characters (\s is for blanks)
      \d stands for digits
      * is a modifier that means 'zero or more of the previous type of character'
      + is a modifier that means 'one  or more of the previous type of character'
      ? is a modifier that means 'zero or one  of the previous type of character'
      () captures whatever is inside for use via $1, $2, $3, etc
   For example:
    $data = 'Mar 25, 1998 - 11:53 AM EST / 1998.03.25 1653 UTC';
    $date = $1, $year = $2, $time = $3 if $data =~ /(.+), (\d\d\d\d) - (.+) EST /;
   On Windows systems, path names can use the Unix convention of / instead
   of the DOS convention of \.

List of supported hardware interfaces

For a list of the hardware in Bruce's house, see mh/pod/faq.* question 6.8.

You can get the ActiveHome kit for $50 from at http://www.x10.com . This includes a 2 way X10 CM11 interface a lamp module and a couple of remote controllers. They also sell the CM17 firecracker interface which mh supports, but is a one way interface only (does not receive X10 signals).

X10.com also sells a $50 IR transmitter that works through the CM17 interface. Info is at http://www.x10.com/products/ux17a_bj2.htm. This is supported via the mh IR_Item object.

Another nice X10 option are the X10 RF -> Serial interfaces. The MR26A is $30 from http://www.x10.com/products/x10_mr26a.htm . The W800RF32 is $50 from http://www.wgldesigns.com/w800.html . A comparison of them is here http://www.wgldesigns.com/comments.htm . These will receive the information coming from most of the X10 RF devices (e.g. motion sensors, keychains, palmpads, remote controls like UR19, UR47, JR20). The advantage to receiving X10 data this way, instead of a powerline device like a CM11, is it is much faster as data goes from RF -> MR26A/W800RF32 -> serial_port -> mh. Actions can be triggered in milliseconds, without having to wait for the 500+ millisecond time it takes to transmit data over the powerline. More info in mh/code/common/x10_rf_relay.pl .

This new RF->X10 interface now supports all house codes (older ones only support one house code each): http://www.letsautomate.com/10680.cfm?CFID=67097&CFTOKEN=4528584

Another supported X10 interface is the Marrick Lynx10-PLC controller: http://www.marrickltd.com See mh/lib/Lynx10PLC.pm

A nice summary of the different X10 controllers is here: http://www.geocities.com/ido_bartana/Which_controller.html

A survey of common X-10 devices and some hints as to their behavior is here: http://www.shed.com/x10stuff.html

Here is yet another site with X10 info: http://www.x10ideas.com

Europeans can get X10 stuff from http://www.intellihome.be , http://www.hwg-telekom.de , www.madomotique.com, www.letsautomate.com, and www.laser.com .

Europeans can also get iButton and Weeder boards from http://www.homechip.co.uk

Australians can get gear from www.digihouse.com.au , www.eon3.com , www.dse.com.au , and www.tecsol.com.au .

Note that the CM12 is the equivalent of a CM11 for 220v/240v locations.

Many X10 noise issues can be solved with the LEVHCA02 repeater and bridge, available here for $65: http://www.worthdist.com/Leviton/hca02-10e.htm

Weeder Technologies, http://www.weedtech.com , offers the following kits priced from $30 to $50 each. All of these kits can share the same serial port:

   2 way X10 interface
   12 bit Digital IO (can be input or output, switch or button)
   8 port Analog IO, 10 bit resolution
   Caller ID, name and number
   Outgoing DTMF phone monitor

You can use 1->2 of the Weeder IO kits to drive the the 'universal relay kit' from http://jameco.com (do a search for 'relay card' or 128910). The $60 gets you 8 relays. Here is some more info:

1. The board is sold as 8 relays complete, upgradable to 16. If you only want to use 8 relays(in a limited fashion), the kit is all you need.

2. If you want more than 8, you need more than just the extra 8 relays them selves. - you will need 8 extra diodes(1n4148 in my kit) - you will need about 30 extra terminating blocks

3. The board comes with spots for LEDs to show activity, you will need to provide those as well as the resistors to drive them(none are provided).

4. As delivered, the Weeder board will not drive the relays, you will need to get 2 ULN2803 darligton transistor array chips to do that(The relays need 12V, the Weeders give 5V).

5. The board has the ability to be manually driven, you will need to buy 2 extra 8 pin dip switches to do that.

6. It comes as a bare open board, so think about an enclosure of some type.

7. All of these parts are available from jameco for nominal extra cost.

Danal Estes reports another relay option is the ELK-924-4 ``Sensitive Relay'': http://www.elkproducts.com/products/elk-924.htm. He used a couple of the four packs. Work like a charm, have been in an attic for over a year, no problems. Smarthome carries these for $38 a four-pack, you can probably find them cheaper, lots of alarm system places carry them. Search on ``ELK-924''.

Nico wrote an interface to the Velleman k8000 board: http://www.velleman.be/Product.asp?lan=1&id=9383, available from his site at http://yow.be

The HomeBase.pm module to supports the JDS interfaces, http://www.jdstechnologies.com . See member mh/code/public/test_homebase.pl for example code.

The Homevison.pm module to supports HomeVision controller, http://www.csi3.com/homevis2.htm . This controller does 2 way X10, 2 way IR, 24 digital IO, and can output its menus to TV. See member mh/code/public/test_homevision.pl for example code.

The DSC_Alarm.pm module supports the DSC PC5400 serial printer interface. This module prints all events that DSC logs to the event buffer. These events include arms/disarms/alarms, user code(s) that performed action(s), installer access, etc. DSC does not log individual zone open/close nor 'entry chime' events. See the mh/lib/DSC_Alarm.pm for further information. See also the DSC_Alarm item below.

The CPU-XA, Ocelot, and Leopard controllers from Applied Digital Inc (http://www.appdig.com/adicon.html ) are supported through network calls to the cpuxad daemon, part of the XALIB package, available at http://mywebpages.comcast.net/ncherry/common/cpuxad

There is a $30 (assembled) pic base IR receiver you can order from the Netherlands ($3 shipping):

  http://www.evation.com/irman/

In addition to the mh/code/public/irman.pl code, there is free winamp, windows, and linux software for it. The schematics that it is built from are also posted on the net (elsewhere), but for $30, it hardly seems worth building it from scratch.

There is an X10 product called IR Commander that works with the Firecracker and sends infrared. It's not a learning remote and most people who try it are disapointed. The RedRat2 is supported by misterhouse, but doesn't seem too popular. David Norwood wrote the module for the UIRT2 and it is being used by a few on both Windows and Linux. He now also has a module for the USB-UIRT. The Ocelot is another option.

  FireCracker: http://www.x10.com/products/ux17a_bj2.htm
  RedRat: http://www.redrat.co.uk/
  UIRT: http://www.fukushima.us/UIRT2/
  UIRT USB: http://home.earthlink.net/~jrhees/USBUIRT/index.htm

As of 6/2004, the send/receive USB RedRat3 ($99 from http://www.redrat.co.uk/ ) is supported using the windows xAP RedRat Connector: http://www.xapframework.net/modules.php?name=Downloads&d_op=viewdownload&cid=45. RedRat3 is a great product with a growing online list of downloadable IR codes.

As of 6/2005, the send/receive multi-zone capable USB IRTrans interface (99 Euro from http://www.irtrans.com/) supports xAP, and thus MisterHouse.

Here are 2 LCD displays with programmable serial interfaces:

  http://www.crystalfontz.com/products/634/index.html  $60
  http://linuxcentral.com  $80 ($101 with keypad interface)

The first display is a bit newer, bigger, brighter, cheaper. But the second one has a version that include a 4x4 keypad interface (keypad available from DigiKey for $20). Both are 4 lines x 20 characters (they also have a 4x40, but no keypad interface). I ordered the 2nd one from linuxcentral, with a keypad from http://www.digikey.com (pn CKN6011-ND or GH5010-ND). A comparison of the 2, plus some nifty linux software for them, can be found at http://lcdproc.omnipotent.net

An example on how to use these displays is in mh/code/bruce/lcdproc.pl. See the header of that file for more info.

Here are a few other devices which look useful, but have not had code written for them:

 http://www.nirvis.com/  $180 2-way IR device
 http://www.cc-concepts.com/  iButton DigiLock interface as electronic keychain

Nice big Alpha LED serial connnected signs are supported as of 6/2004. I picked up the Alpha 213C (a.k.a. BetaBrite, 2`` x 24'', 14 character) from the local Sam's Club for $150. Their web site only lists a bigger version, but info on BetaBrite I have can be seen here: http://www.betabrite.com/Pages/betabrite.htm . It displays in multiple colors, in either scrolling or fixed text. See Display_Alpha.pm header for details.

The Serial_Item object can read and write to any serially connect device. There is example code in the code/public directory that shows how to interface to a ham radio TNC receiver to decode and track a GPS transmitters position.

An better, object-oriented way to write code to interface to a new hardware device is in mh/lib/example_interface.pm.

Byterunner offers lots of types of serial cards: http://www.byterunner.com. Note for windows users: with the PCI-800H, I found I had to click the 'use default baud rate' box under control panel -> device manager (under the 4th tab, data rate).

Good sources of information on the WX200/WM918 weather stations are at http://wx200.planetfall.com/ and http://www.weatherwatchers.org/wxstation/WX-200/ . $200->$300 at Radio Shack or $200 from http://www.heartlandamerica.com (search for weather, model WW22- 3350). See mh/lib/Weather_wx200* for info on interface code.

There is also now a new WMR-918/968/Radio Shack Accuweather, which is a wireless version, available for about $350. See mh/code/public/weather_monitor_wmr968.pl.

See weather_monitor_ultimeter2000.pl for an example of interfacing to the Peet Bros. Ultimeter200 weather station: http://www.peetbros.com

iButton is a family of devices that can all talk to a '1 wire' interface. Here are some URLs and prices:

  http://www.ibutton.com (pick shop online)
    - $15 DS9097U-009 or -S09 + DS1402D-DR8l for the serial interface
      Note:  The DS9097, DS9097E, or parallel port adapters will not work with mh
    -  $3 DS1990A-F5  for 64 bit ID buttons.
  http://www.ibuttonlink.com
    - The $35 LINK interface has been reported as more reliable and
      is more flexable than the DS9097 listed above.
  http://www.pointsix.com
    - They also sell the $15 interface (under iButton Products)
    - $10 for a 1 port relay card
    - $10 for a 2 port digital input or output card
    - $6  for a 1820 temperature sensor
    - Info on how to wire up a 1 wire bus:
      http://www.pointsix.com/cgi-bin/PointSix.cgi?AppNotes&app010
  http://www.aag.com.mx
    - Sells an iButton weather station for $80
  http://ibuttonlink.com/
    - An improved $35 serial interface
  http://weather.henriksens.net  has free 1-Wire Weather station software
    - It includes a tcp/ip server that (in theory) could talk to mh
  Ray Dzek has a nice how to get started tutorial here:
    http://www.solarbugs.com/home/ibutton.htm
  Neil Wrightson also has some 1wire info here:
    http://www.users.bigpond.com/nwrightson/1Wire%20Hardware.htm
  A Linux "One Wire File System" that sets up the one wire bus to look like
  a directory (not integrated to mh yet)
    http://owfs.sourceforge.net/

If you want more reliable VR without having to use a headset, you can try these to digital array microphones:

 - Labtec LVA-7280 ClearVoice Digital Microphone ($130)
   http://www.labtec.com/product/family.cfm?CategoryID=2
 - Andrea Desktop array DA-400 ($150)
   http://www.andreaelectronics.com

The Labtec has a push button on top to turn it on and off and a red/green LED that blinks green when sound is heard. The Andrea has a slide switch on the side to turn it on and off. The Andrea has a bit clearer sound and is about 2/3 the size. Having a hardware switch to disable VR is handy.

Both of these mikes are meant to be used when you are at the desktop. The idea is that they filter out background noise better, so you can use them in slightly noise environments, where headsets were normally required.

The mh/code/common/barcode* members code will process data from the Cue Kat keyboard simulator barcode scanner, which used to be free from places like Radio Shack but are now discontinued.

If you would like to do call waiting caller id (normal callerid and caller id even while you are on the phone), you can a good deal on the NetCallerID box for $15 from here: http://www.dfwmetrotechs.com/misterhouse/calleridorder.html

Varous routers can be monitored and controled with code/common/monitor_router*.pl

5/2002: Bill Sobel added lib/AudiotronPlayer.pm, to support the Turtle Beach AudioTron.

5/2002: Pete Flaherty's created code/public/weather_iB_OWW_client.pl to read data from the OWW 1-wire ibutton weather station daemon

8/2002: Chris Witte created mh/lib/caddx.pm and mh/code/public/caddx.* for monitoring the outputs from a CADDX nx8e alarm panel.

9/2002: Richard Morgan's sent in mh/code/public/cbus_v2.0.zip, his set of code for talking to CBUS devices via the Australian Clipsal CGATE interface.

7/2003: Lincoln Foreman sent in mh/code/public/pha_k256.pl as an example of reading data from Phanderson's K256 kit which can monitor 256 DS1820 temperature sensors, 8 bits digital IO, and 11 bits of analog IO. Available for $50 from http://www.phanderson.com/t64.html

12/2003: Kirk Bauer created lib/RCSsTR40.pm for the RCS TR40 Theormostate: http://www.resconsys.com/products/stats/serial.htm

1/2004: Nick Maddock replaced mh/code/public/alarm_concept.* files with mh/code/public/concept_* for interfacing to the Concept 4000 alarm panel.

3/2004: Kirk Bauer created lib/Musica.pm to control the Musica whole-house audio system by Netstreams over the RS232 port. This system has provides 6 zones and 4 sources with very nice keypads: http://www.netstreams.com

4/2004: Dave Hall created code/public/robot_er1.pl for interacting with the Evolution Robotics ER1 robot: http://www.evolution.com/er1/

7/2004: Kirk Bauer created lib/Tivo_Control.pm for displaying text on a Tivo. See header for details.

7/204: Created code/common/robot_robosapien.pl for using IR to control the RoboSapien Robot: http://www.robosapienonline.com (got mine for $100 at Best Buy). It doesn't do anything practical, but it is a fun toy.

9/2004: Scott Johnson created mh/code/public/siteplayer.pl as an example of sending digital signals to a $30 siteplayer ethernet interface: http://www.siteplayer.com

11/2004: Created lib/Servo_Item.pm to supports servo motors via the $40 Mini SSC II serial servo control board, available from http://seetron.com .

11/2004: Created code/common/robot_esra.pl to control the $180 Robodyssy ESRA robot head from http://robodyssey.com . It has 5 servos (eye lid, eyes, lip top/bottom, and an optional neck) which are connected via the Mini SSC II serial servo controler. Pictures and a movie are at http://misterhouse.net/public/robot/ .

5/2005: Created mh/bin/xAP-bluetooth.pl (called with mh/bin/xAP-bluetooth) and mh/code/common/xAP_bluetooth.pl for monitoring when bluetooth enabled devices (e.g. phones) go in and out of range. The xAP daemon runs on Linux and requires the bluez lib to be installed (pointers in the header).

10/2005: Dan Wilga created mh/lib/X10_Wish.pm which interfaces to the Linux Wish X10 drivers: http://sourceforge.net/projects/wish . These are the currently supported device: PowerLinc Serial, PowerLinc 1132 USB, CM11A, and Firecracker/CM17A. Currently supports sending, but not receiving, X10 data. More info at the top of lib/X10_Wish.pm.

10/2005: Brent DeShazer created lib/Display_osd232.pl and code/common/display_osd232.pl for interfacing with Intuitive Curciuts OSD-232 On-screen video overlay interface, available from http://www.icircuits.com/prod_osd232.html

10/2005: Peter Sjodin added Linux support for EIB (European Installation Bus, info here: http://en.wikipedia.org/wiki/European_Installation_Bus ). New modules are lib/EIB_Items.pm and lib/EIB_Device.pm. Example items are in code/examples/eib_items.mht.

Info on an LCD internet appliances that makes an affordable touch screen controllers in the MisterHouse Internet Appliance FAQ here: mh/docs/ia.html

List of startup options

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:

code_dir=\mh\code\test
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.

html_root=$Pgm_Root/web
html_file=mh_default/index.html
Points to where the web interface html files are.

http_port=8080
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 ).

server_telnet_port=23
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'.

diagnostics=1
Set this (or w=1) to enable perl -w warning messages on potential code errors. mh runs about 10% slower with this option on.

run=perl_script
This parms allow the compile mh.exe to act as a general purpose perl interpreter, so we can run any arbitrary perl script, without having to have perl installed. See the get_weather.bat, get_tv_grid.bat, or set_clock.bat files for examples. This also allows is to use the mh run command to spawn a separate mh process to run longer running perl steps, even if we do not have perl installed. A Win32 'fork' command would be cleaner, but alas, no forks.

debug=
log=
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

time_start=
time_stop=
time_increment=
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

List of global variables

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 mh/bin/mh.

The %Moon array has $Moon{phase}, $Moon{brightness}, $Moon{age}, and $Moon{new,first,full,last} entries.

The $Time_Now and $Date_Now variables return a 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';

List of objects and their methods

File_Item
You can use File_Item to read a line of data from a file. Note: These methods currently read the entire file, so if have big files (say, >1 meg) we want to read, we should invent some new methods.
  Methods:
     new('file_name')
     name         : Returns 'file_name'
     read_all     : Returns contents for the file. If used in a list context,
                    a list is returned, otherwise a string of all the lines.
     read_head    : Returns the first few lines of a file.  See file_head.
     read_tail    : Returns the last  few lines of a file.  See file_tail.
     read_random  : Reads a random record.
                    This also re-sets the index to the random position.
     read_next      : Reads the next record, according to the index.
                      After reading the last record, it wraps back to the first.
     read_next_tail : Like read_next, except, it will not wrap back to
                      the first record (i.e. after reaching the end of the file,
                      it will alwasy return the last record).
     read_current   : Reads the current record, according to the index.
     get_index    : Which record was last read.  A couple of notes:
                      - The index is saved between mh sessions.
                      - If you use a File_Item that does not yet have
                        an index set, a random index will be used and stored.
     set_index    : Set the index.
     set_watch('flag') : Sets the 'changed' time check.
     changed      : Returns 0 if the file was not changed since a set_watch call.
                    When the file changes:
                     - If 'flag' was specified in the new method, 'flag' is returned.
                     - Otherwise, it returns the number of seconds since a set_watch call.
     exist        : Returns 1 if the file exists
     exist_now    : Returns 1 if the file was created since the last exist_now test.
     said         : Returns data added to a file since the last call.
                    Only one record is returned per call.
                    This is useful for monitoring log files.
                    See mh/code/bruce/shoutcast_monitor.pl  for an example.
  Examples:
     $f_deep_thoughts = new File_Item("$Pgm_Root/data/remarks/deep_thoughts.txt");
     my $thought = read_next $f_deep_thoughts;
     set_index $f_deep_thoughts 1;
     $f_weather_forecast = new File_Item("$Pgm_Root/data/web/weather_forecast.txt");
     set_watch $f_weather_forecast;
     display name $f_weather_forecast if changed $f_weather_forecast;
     $shoutcast_log = new File_Item 'd:/shoutcast/sc_serv.log';
     print "Log data: $state" if $New_Second and $state = said $shoutcast_log;

Generic_Item
You can use this object to store and query arbitrary data. This is more useful than 'my' variables, if you need to share data between code files, since 'my' variables are local to a code file. States of these items are also saved/restored when mh is stopped/started.

All other items that have states (e.g. X10_Item, Serial_Item, iButton, Voice_Cmd, Group) inherit all Generic_Item methods.

  Methods:
     new           :  Creates an object of the specified name
     state         :  Returns the state (e.g. on, off)
     state_now     :  True only for one pass after object state is set.
     state_changed :  True only for one pass after object state changes.
                      Unlike state_now, state_changed will return undef (false) if
                      the item is set to the same state it is currently in.
     state_final   :  Returns the state the object will be in after all queued
                      state changes have been processed.
     set($state)   :  Places the value into the state field (e.g. set $light on)
                      Note: states are set at the start of the next mh pass.
     set_now($state) :  Like set, except states are set when called, not on the next pass.
     set_with_timer($state, $time, $return_state)
                   :  Like set, but will return to $return_state after $time.
                      If $return_state is not specified, it toggles $state.
                      If $return_state is previous, it returns to the previous state.
                      If set is called before the timer expires, the timer will be unset.
        You can also stack a series of set_with_timer calls with one set call like this:
           set('s1~t1~s2~t2...sn');
        where s1, s2, ... sn are various states, and t1, t2 ... tn are the times to
        wait between states.  See example below.
        If you want to stack a set of states without delays, you use ; like this:
           set('on~2~random:on;repeat:on;play');
        See mh/code/examples/test_states_stacked.pl for a complete example.
        Note, this is turnded off by default for Serial_Item objects.
        To enable, run:   $object -> state_overload(ON);
     set_casesensitive   : By default, states are all lowercased, to allow for case
                           insensitive tests.  To avoid this (for example on Serial Interfaces
                           that are case sensitive), call this method.
     hidden(1/0)         : If set to 1, the object will not show up on Tk or Web menus.
     tie_items($item)    : If the state of the generic_item changes, then
                           the state of $item will be set to that same state
     untie_items($item)  : Untie $item
     tie_event($code)   : It the state of the generic_item changes, then
                           Code string $code will trigger, with the variable
                           $state getting expanded.
     untie_items($code)  : Untie $code
     tie_filter($filter) : Use this to disable control of the item whenever
                           $filter returns true.   Variables $state and $set_by
                           can be used in the $filter test.
     untie_filter($filter):Untie $filter
     tie_time($time,$state): Sets item to $state if the $time string evaluates true.
                           $time can be in either time_cron or time_now format.
     untie_time($time)   : Untie $time
     time_idle($time)    : Returns true when the object has had no state changes
                           since the specified time. $time can be be in seconds,
                           minutes, hours, or days (e.g. '90 s' or '7 days').
                           Optionally, $time can also specify a spefic state (e.g. '4 m on')
     set_by              : allows setting a description of why the last state change
                             - motion, sunrise, manual, serial,    etc..  any string
                               is allowed value is returned by get_set_by below.
     get_idle_time       : Returns number of seconds since the last state change
     get_set_by          : Returns what caused this object to change.  Values either:
                            - Name of the object
                            - web, tk, telnet, vr, xcmd, serial
                           An example is in mh/code/examples/test_set_by.pl
     set_states(@states) : Sets valid states to @states
     add_states(@states) : Adds @states to the list of valid states
     get_states          : Returns the list of valid states
     restore_data(@vars) : Specifies which variables should be saved/restored between
                           mh reload/restarts.  The state var is always saved.
     set_authority('anyone') : Bypasses password control
     set_icon : Point to the icon member you want the web interface to use.
                See the 'Customizing the web interface' section of this document for details.
     set_info : Adds additional information.  This will show up as a popup window
                on the web interface, when the mouse hovers over the command text.
     set_label: Specify a text label, useful for creating touch screen interfaces.
     set_web_style: Contols the style of we form used.  Can be dropdown, radio, or url.
                    See mh/code/examples/test_web_styles.pl
     set_count   : Use these methods to control a count variable.
     get_count
     incr_count
     reset_count

The tie/untie methods can be done conditionally, so you can change what is tied to what on the fly. If you do this, however, you need to also make sure you also make the tie* method call on $Reload, as the objects tie items/events are currently not stored between reloads.

The tie_* methods can also take 2 optional arguments. The first is the state you want the tie to apply to. If not specified, it defaults to all states. This is required for tie_time.

The 2nd optional tie_* argument is a print_log message you want whenever the tie_ event triggers.

  Examples:
     $tv_grid = new Generic_Item;
     set $tv_grid 'channel 2 from 7:00 to 8:00 on 1/24 for This Old House';
     speak "tv set to $state" if $state = state_now $tv_grid;
     $wakeup_time = new Generic_Item;
     speak "Your wakeup time is set for $state" if $state = state_now $wakeup_time;
     speak "Time to wake up" if time_now state $wakeup_time;
   # Since Voice_Cmd objects inherit Generic_Items methods, we can
   # use tie_items and tie_event like this:
     $indoor_fountain    = new X10_Appliance('OB');
     $v_indoor_fountain  = new Voice_Cmd 'Indoor fountain [on,off]';
     $v_indoor_fountain -> tie_items($indoor_fountain);
     $v_indoor_fountain -> tie_event('speak "Ok, fountain was turned $state"');
     if ($state = state_now $test_button) {
        my $ref = get_set_by $test_button;
        print_log "Test button's was set to $state by $ref->{object_name}" if $ref;
     }
                           # This shows how to set states
    $TV  = new IR_Item 'TV';
    my  $tv_states = 'power,on,off,mute,vol+,vol-,ch+,ch-';
    set_states  $TV split ',', $tv_states;  # causes $tv_states to be split into array
                                                           values via the split command deliminated by the ,
    $v_tv_control = new  Voice_Cmd("tv [$tv_states]");
                           # This shows how to save persistant arbitrary data
    $Test = new Generic_Item;
    $Test->{junk_data1} = $Time_Now if $Reload;
    $Test->{junk_data2} = $Month    if $Reload;
    $Test->restore_data('junk_data1', 'junk_data2');
                           # Here are some tie* examples
    $fountain -> tie_filter('state $windy eq ON', ON,
                            'Overriding item1 ON command because of wind');
    $fountain -> tie_time('10PM', OFF, 'Fountain turned off');
                           # Disable an item when we are away
    $item1 -> tie_filter('state $status_away eq ON');
    $item1 -> tie_event('print_log "item1 toggled to $state"');
                           # Ignore RF sourced (e.g. W800 or MR26) X10 data
    $light1 -> tie_filter('$set_by eq "rf"', , 'Ignoring x10 rf data');
                           # Disable callerid announcements when we are not at home
    $cid_announce -> tie_filter('state $mode_occupied ne "home"');
                           # Set an item to multiple states, with 5 second a delay between states
    $test_set1  = new Generic_Item;
    $test_set1 -> set('0%~5~30%~5~60%~5~100%') if new_second 20;
    $test_set1 -> tie_event('print_log "test set $state"');
    print 'Item is idle' if $test_set1 -> time_idle('4 seconds');

See mh/code/examples/generic_item.pl for more examples, test_tie.pl for more examples on how to tie/untie items/events, and test_idle.pl for more examples on testing idle times.

Group
You can use this object to group and operate on groups of items:
  Methods:
     new(@item_list)
     add(@item_list)
     list
     set
     state
     state_now    :  Like the Generic_Item methods, these return
                     the last state that the group was set to.
                     If a group member changed, these methods will
                     return 'member $state_name' rather than just '$state_name'.
                     You can use the member_changed or get_set_by methods
                     to see which member changed.
     state_log    : Returns a list array of the last max_state_log_entries (mh.ini parm)
                    time_date stamped states.
     member_changed:      Returns a member object name whenever one changes
     member_changed_log:  Returns a list of recenty changed members.
                          The first one was the most recently changed.
     remove       : Remove an item from a group
  Examples:
     $outside_lights = new Group($light1, $light2, $light3);
     $outside_lights-> add($light4, $light5);
     $outside_lights-> add($light6);
     set $outside_lights ON if time_now("$Time_Sunset + 0:15");
     for my $item (list $outside_lights) {
         print "member = $item->{object_name}\n";
     }
     if (my $member_name = member_changed $outside_lights) {
        my $member = &get_object_by_name($member_name);
        print_log "Group member $member_name changed to $member->{state}"
     }
     my @members = member_changed_log $sensors;
     # turn off all but the bedroom light when we are getting ready for bed
     if( state_now $bedroom_control eq ON ) {
        my $all = new Group(list $All_Lights);
        $all -> remove ( $master_bedroom );
        set $master_bedroom ON;
        set $all OFF;
     }

See mh/code/examples/test_group.pl and mh/code/public/monitor_occupancy_jason.pl for more examples.

iButton_Item
This is used to query and/or control an iButton device (for more info on iButton, see the hardware section). To enable iButton support in mh, set the mh.ini parm ibutton_port.
  Methods:
     new $id, $port, $channel
       If $port is not specified, the port of the first iButton::connect
       will be used.
       $channel (used for switches like the DS2406) defaults to A
     set $state   : Sets the item to the specified state.
     state        : Returns the last state that was received or sent
     state_now    : Returns the state that was received or sent in the current pass.
     state_log    : Returns a list array of the last max_state_log_entries (mh.ini parm)
                    time_date stamped states.
     read_temp    : Returns the temperature of temperature devices.
     read_switch  : Reads iButton switch data
     read_windspeed : Reads iButton weather station wind speed
     read_dir       : Reads iButton weather station wind direction

In addition to the above, all of the methods provided by the Hardware/iButton/Device.pm module are available (documented in mh/lib/site/Hardware/iButton/Device.pm).

These functions are also part of the iButton module, but not associated with an object:

     scan        $family, $port : Returns a object list of iButton devices that match $family
     scan_report $family, $port : Returns a report of      iButton devices that match $family
     monitor $family, $port : Checks the one wire bus for iButton activity
     connect $port       : Connect to the one wire bus.  Note you can now have multiple
                           iButton interfaces on multiple COM ports.
     disconnect          : Disconnect to the one wire bus

Note, all of these functions take an optional $port parm (required for connect). If not specified, the port of the first connect record will be used.

The $id required when creating new iButton_Item is the 16 hex character (64 bit) iButton id that is unique to every device. This is often printed on the larger devices. If not, you can use:

    $v_iButton_list      = new Voice_Cmd "List all the iButton buttons";
    print_log &iButton::scan_report if said $v_iButton_list;

The last 2 characters are CRC bits and are optional. If missing (i.e. you only specify the first 14 characters), mh will calculate it.

The first 2 characters are the iButton family type. Here is a list of family types:

     Field Index:
      ------------
      (1) Family code in hex
      (2) Number of regular memory pages
      (3) Length of regular memory page in bytes
      (4) Number of status memory pages
      (5) Length of status memory page in bytes
      (6) Max communication speed (0 regular, 1 Overdrive)
      (7) Memory type (see below)
      (8) Part number in iButton package
      (9) Part number in non-iButton package
      (10) Brief descriptions
      (1)   (2)  (3)  (4)  (5)  (6)  (7)   (8)   (9)   (10)
      -------------------------------------------------------
      01,    0,   0,   0,   0,   1,   0, DS1990A,DS2401,Unique Serial Number
      02,    0,   0,   0,   0,   0,   0, DS1991,DS1205, MultiKey iButton
      04,   16,  32,   0,   0,   0,   1, DS1994,DS2404,4K-bit NVRAM with Clock
      05,    0,   0,   0,   0,   0,   0, DS2405,,Single Addressable Switch
      06,   16,  32,   0,   0,   0,   1, DS1993,DS2403,4K-bit NVRAM
      08,    4,  32,   0,   0,   0,   1, DS1992,DS2402,1K-bit NVRAM
      09,    4,  32,   1,   8,   1,   2, DS1982,DS2502,1K-bit EPROM
      0A,   64,  32,   0,   0,   1,   1, DS1995,DS2416,16K-bit NVRAM
      0B,   64,  32,  40,   8,   1,   3, DS1985,DS2505,16K-bit EPROM
      0C,  256,  32,   0,   0,   1,   1, DS1996,DS2464,64K-bit NVRAM
      0F,  256,  32,  64,   8,   1,   3, DS1986,DS2506,64K-bit EPROM
      10,    0,   0,   0,   0,   0,   0, DS1920,DS1820,Temperature iButton with Trips
      11,    2,  32,   1,   8,   0,   2, DS1981,DS2501,512-bit EPROM
      12,    4,  32,   1,   8,   0,   4, DS2407,,Dual Addressable Switch
      13,   16,  32,  34,   8,   0,   3, DS1983,DS2503,4K-bit EPROM
      14,    1,  32,   0,   0,   0,   5, DS1971,DS2430A,256-bit EEPROM, plus
      64-bit
      OTP
      15,    0,   0,   0,   0,   1,   0, DS87C900,,Lock Processor
      16,    0,   0,   0,   0,   0,   0, DS1954,,Crypto iButton
      18,    4,  32,   0,   0,   1,   6, DS1963S,4K-bit Transaction iButton with
      SHA
      1A,   16,  32,   0,   0,   1,   6, DS1963,,4K-bit Transaction iButton
      1C,    4,  32,   0,   0,   1,   6, DS2422,,1K-bit EconoRAM with Counter
      Input
      1D,   16,  32,   0,   0,   1,   6, DS2423,,4K-bit EconoRAM with Counter
      Input
      1F,    0,  32,   0,   0,   0,   0, DS2409,,One-Wire Net Coupler
      20,    3,   8,   0,   0,   1,   9, DS2450,,Quad A-D Converter
      21,   16,  32,   0,   0,   1,   8, DS1921,,Temperature Recorder iButton
      23,   16,  32,   0,   0,   1,   7, DS1973,DS2433,4K-bit EEPROM
      22                                 DS1822 temperature button
      40,   16,  32,   0,   0,   0,   1, DS1608,,Battery Pack Clock
  Examples:
    $v_iButton_connect = new Voice_Cmd "[Connect,Disconnect] to the iButton bus";
    if ($state = said $v_iButton_connect) {
      if ($state eq 'Connect') {
        print_log &iButton::connect($config_parms{iButton_serial_port});
        print_log &iButton::connect($config_parms{iButton_2_serial_port});
      }
      else {
        print_log &iButton::disconnect;
        print_log &iButton::disconnect, $config_parms{iButton_2_serial_port};
      }
    }
    $ib_bruce  = new iButton '010000012345ef';
    speak 'Hi Bruce'  if ON  eq state_now $ib_bruce;
    speak 'Later'     if OFF eq state_now $ib_bruce;
    $ib_relay1 = new iButton '12000000123456ff', undef, 'A';
    $ib_relay2 = new iButton '12000000123456ff', undef, 'B';
    $v_iButton_relay1    = new Voice_Cmd "Turn relay1 [on,off]";
    if ($state = said $v_iButton_relay1) {
       print_log "Setting iButton relay1 to $state";
       set $ib_relay1 $state;
    }
    $ib_temp1  = new iButton '1000000029a14f', $config_parms{iButton_2_serial_port};
    $ib_temp2  = new iButton '1000000029f5d6';
    my @ib_temps = ($ib_temp1, $ib_temp2);
    $v_iButton_readtemp  = new Voice_Cmd "Read the iButton temperature [1,2]";
    if ($state = said $v_iButton_readtemp) {
       my $b = $ib_temps[$state-1];
       my $temp = read_temp $b;
       print_log "Temp for sensor $state: $temp F";
       logit("$config_parms{data_dir}/iButton_temps.log",  "$state: $temp");
    }
    if ($New_Second and !($Minute % 5)) {
       run_voice_cmd 'Read the iButton temperature 1' if $Second == 11;
       run_voice_cmd 'Read the iButton temperature 2' if $Second == 22;
    }

HVweb_Item
This object allows control of Homevision controller via the Homevision web server. Set homevision_url=<your homevision web server URL> in mh.ini. See the Homevision documentation for complete list of command formats. Configure Homevision Webserver to report command results.
  Examples:
     $kitchen_light    =  new HVweb_Item('On',    'X10 G1 On');
     $kitchen_light    -> add           ('Off',   'X10 G1 Off');
     $vcr              =  new HVweb_Item('Power', 'IR 45 1 time');
     $vcr              -> add           ('Play',  'IR 46 1 time');
     set $kitchen_light 'On';
     set $vcr 'Play';

IR_Item
This object controls IR transmiters. The devices currently supported are the X10 IR Commander, HomeVision, CPU-XA/Ocelot/Leopard, and UIRT2 (http://www.fukushima.us/UIRT2/).

The X10 IR Commander (http://www.x10.com/products/ux17a_bj2.htm) receives commands from the wireless CM17 (firecracker) interface. Currently, you must use the X10 supplied software (http://www.x10.com/commander.htm and/or ftp://ftp.x10.com/pub/applications/commander/) to program the IR Commander to use the codes for your various remotes.

  Methods:
     new $type, $code, $interface, $mapref : Creates a new Item.
        $type is the type of device being controlled.  Default is TV.
        Here are the only valid types for the X10 IR Commander:
           TV, VCR, CAB, CD, SAT, DVD
        $code specifies how numbers will be treated.  Default is 2digit.
           noPad   : numbers are not modified
           2digit  : single digits will be padded with a leading zero
           3digit  : numbers will be padded to 3 digits with leading zeros
           addEnter: adds an ENTER command after any numbers, to make
                     changing channels faster on devices that wait for a
                     timeout (e.g. Sony TVs).
        $interface is the transmitter to be used.  Default is cm17.
           cm17    : X10 IR Commander
           homevision: HomeVision
           ncpuxa  : CPU-XA/Ocelot/Leopard
           uirt2   : UIRT2 (http://www.fukushima.us/UIRT2/)
           xAP     : Sends / receives data via the xAP protocol.
        $mapref is a reference to a hash that specifies commands to be
           mapped to other commands.  This is useful for transmitters
           like the Ocelot which use slot numbers instead of device and
             function name pairs.  Default is a reference to a hash containing
                ON  => POWER,
                OFF => POWER
             Which you would not want if you had descrete ON and OFF codes.
     state        : Returns the last state that was sent
     state_now    : Returns the state that was sent in the current pass.
     state_log    : Returns a list array of the last max_state_log_entries (mh.ini parm)
                    time_date stamped states.
     set $command : Sends out commands to the IR device.  Here is a list of valid commands for the X10 IR Commander::
                    Note:  Commands are not case insensitive
        POWER      MUTE
        CH+        CH-
        VOL+       VOL-
        1          2
        3          4
        5          6
        7          8
        9          0
        MENU       ENTER
        FF         REW
        RECORD     PAUSE
        PLAY       STOP
        AVSWITCH   DISPLAY
        UP         DOWN
        LEFT       RIGHT
        SKIPDOWN   SKIPUP
        TITLE      SUBTITLE
        EXIT       OK
        RETURN
  Examples:
    $TV = new IR_Item 'TV';
    $v_tv_control = new  Voice_Cmd("tv [power,on,off,mute,vol+,vol-,ch+,ch-]");
    set $TV $state if $state = said $v_tv_control;
    $VCR = new IR_Item 'vcr', '3digit';
    set $VCR "12,RECORD" if time_cron('59 19 * * 3');
    set $VCR "STOP"      if time_cron('00 20 * * 3');

LCD
This object is used to send and receive data to LCD type displays with keypads. An example is in mh/code/bruce/lcd.pl. To use simulate an LCD keypad with your pc keyboard, use mh/code/bruce/lcd_keyboard.pl.
  Methods:
     new $type, $port, $size, $menu_group, $keymap
       $type:  Either lcdproc or keyboard.
       $port:  The ip:port of where lcdproc is running.
       $size:  ROWSxCOLUMNS of the LCD display.
       $menu_group:  The menu parsed by menu_parse (see Menu section of this doc).
       $keymap: A has that translates keys to usable names.
     start        => Connect to the LCD.  Automatically called on startup
     stop         => Disconnect the LCD.
     load $menu   => Load menu $menu.  Default menu is the first one.
     check_key    => Returns whatever key is keyed in
     set @data    => Sends @data to the LCD, one line per list element
     set_key $key => Simulates the keyboard being pressed with $key
     inactive     => Returns true if no key has been pressed in 10 seconds.
  Examples:
     my %lcd_keymap1 = ( N => 'up', I => 'down', M => 'left', H => 'right',
                         F => 'exit', K => 'enter', L => 'left', G => 'right');
     my %lcd_keymap2 = ( 38=> 'up', 40=> 'down', 37=> 'left',
                         39=> 'right', 17=> 'exit', 96=> 'enter') ;
    $lcd1 = new LCD  'lcdproc', '192.168.0.5:13666', '4x20', 'default', \%lcd_keymap1;
    $lcd2 = new LCD 'keyboard',               undef, '4x20', 'mh',      \%lcd_keymap2;

Network_Item
This module doesn't do much yet. Currently uses ping to detect when specified ip address is up.
  Methods:
     new $address, $interval
       $address:  Ip address of the box you want to ping
       $interval: How often to ping (in seconds)
  Examples:
    $network_house = new Network_Item('192.168.0.2',  10);
    $network_hp    = new Network_Item('192.168.0.66', 20);
    print_log "house just changed to $state" if      $state = state_changed $network_house;
    print_log "house is $state" if new_second 15 and $state = state $network_house;

Process_Item
You can use this object to run external programs. On Win32 systems, the Win32::Process function is used. On Unix systems, the fork function is used. On either system, the following methods work in the same way:
  Methods:
     new('program1 arguments', 'program2 arguments', ...)
     set('program1 arguments', 'program2 arguments', ...)
     add('program3 arguments', 'program4 arguments', ...)
        If you specify more than one program, they are run sequentially.  done_now returns
        1 after the last program is done.
        If program starts with &, then 'program arguments' is eval-ed as an internal mh function.
        Otherwise, 'program arguments' is run as an external command.
        On Windows, the &-> eval trick is supposed to work with perl 5.6+ (which has fork), but
        unfortunately, it causes perl to crash often, so is probably not useful yet.
     set_timeout $timeout     : Process will timeout after $timeout seconds
     set_output  $output_file : Program STDOUT errata goes to $output_file
     set_errlog  $errlog_file : Program STDERR errata goes to $errlog_file
     start          : Starts the process.
     start 'program arg'  : Starts the process with program arg
     stop           : Stops the process. If called as a stand alone function
                      (not as an object method), all active Process_Items are stopped.
     done           : Returns the time (seconds since epoch) that the process finished.  If
                      the process has been started, but has not yet finished, it returns 0.
     done_now       : Is true for the pass that the process finished on.
     pid            : Returns the process id
     timed_out      : Returns the time when the process timed out.
                      done_now will still trigger for a timed_out process.
  Examples:
     my $slashdot_news = "$Pgm_Root/data/web/slashdot_news.txt";
     $p_slashdot_news = new Process_Item("get_slashdot_news > $slashdot_news");
     start $p_slashdot_news if time_now('6:30 AM');
     display $slashdot_news if done_now $p_slashdot_news;
     $p_report_weblog = new Process_Item;
     $p_report_weblog ->set_output("$config_parms{data_dir}/weblog_results.txt");
     if (time_now '2 AM') {
         set   $p_report_weblog "report_weblog /mh/data/logs/server.$Year_Month_Now.log";
         start $p_report_weblog;
     }
                    # Example of multiple commands
     $test_process1 = new Process_Item;
     set $test_process1 'sleep 1', 'sleep 2';
     add $test_process1 'sleep 1';
                    # Example of running an internal mh subroutine
    $v_test_ftp = new Voice_Cmd 'Test background ftp [get,put]';
    $p_test_ftp = new Process_Item;
    if ($state = said $v_test_ftp) {
      set $p_test_ftp "&main::net_ftp(file => '/tmp/junk1.txt', " .
                      "file_remote => 'incoming/junk1.txt'," .
                      "command => '$state')";
      set_timeout $p_test_ftpb 60*2;
      start $p_test_ftpb;
    }
    print_log "Ftp command done" if done_now $p_test_ftp;

More examples are in mh/code/examples/test_process.pl

RF_Item
An RF_item is created to receive states from X10 security devices and RF TV style remote through a W800RF32 module or an MR26A module (the MR26A only passes TV remote style data through, it does not pass security data).

To configure the W800 or MR26 interfaces, see the comments at the top of mh/lib/X10_MR26.pm and mh/lib/X10_W800.pm.

RF items can be created in the items.mht in the following manner:

     RF,     68,     keychain_remote,                Security
     RF,     System, security_system,                Security
     RF,     Sensor, security_sensors,               Security
     RF,     81,     door_sensor,                    Security
     RF,     Remote, tv_remote,                      TV

RF Items can be manually created in the following manner:

     $keychain_remote  = new RF_Item('68'    , 'keychain_remote' );
     $security_system  = new RF_Item('system', 'security_system' );
     $security_sensors = new RF_Item('sensor', 'security_sensors');
     $door_sensor      = new RF_Item('81'    , 'door_sensor'     );
     $tv_remote        = new RF_Item('remote', 'tv_remote'       );

The 2nd column the items.mht file (or the 1st parameter when manually creating a new RF_Item) is the 2 digit hexadecimal unit id of the particular transmitter or one of the following classes:

     system     Any device that change the state of the security system.
                States from any transmitters that go into this class are:
                armawaymin, armawaymax, armhomemin, armhomemax, disarm, panic
     sensor     Any device that changes a sensor state.
                States from any transmitters that go into this class are:
                normal, normalmax, normalmin, alert, alertmin, alertmax
     control    Any device that changes some general feature:
                States from any transmitter that go into this class are:
                lightson, lightsoff
     remote     Any TV style remote control (UR51A, etc.).
                States from any transmitter that go into this class are:
                Power PC Title Display Enter Return Up Down Left Right Menu
                Exit Rew Play FF Record Stop Pause Recall 0 1 2 3 4 5 6 7 8 9
                AB Ch+ Ch- Vol- Vol+ Mute

Some transmitters have a min and max switch that cause the transmitter to send different states depending on that switch. If you don't care about the full detail of the state, you can do a test like:

    if (my $state = state_now $door_sensor =~ /^alert/) { ... }

To determine what the 2 digit hexadecimal unit id for a particular security transmetter is, press the button on the transmitter (or open/close the sensor) and look at the misterhouse log to find the id that the unit transmitted.

Serial_Item
Serial_Item is used whenever you want read or write serial port data. Like most mh objects, it inherits all the Generic_Item methods.
  Methods:
     new('data_stream', 'state_name', 'serial_port')
     add('data_stream', 'state_name')
        If 'serial_port' is not specified, then:
           For outgoing X10 data, the first valid port from this list is used:
               cm11, cm17, homevision, homebase, Weeder, serial1, serial2, ...
           For other outgoing data, the first valid port from this list is used:
               Weeder, serial1, serial2, ...
           For incoming data, the 'serial_port' parm is not important, as
           data from the cm11, homebase, and Weeder ports is processed in the same way.
           For the generic ports (serial1, serial2, ...), incoming data is processed
           only by user specified 'said' methods.
     state        : Returns the last state that was received or sent
     state_now    : Returns the state that was received or sent in the current pass.
                    Note that the a 'set' will trigger a state_now one the next pass.
     state_log    : Returns a list array of the last max_state_log_entries (mh.ini parm)
                    time_date stamped states.
     set_dtr      : Sets/resets the DTR line
     set_rcs      : Sets/resets the RCS line.  For example:
                      set_dtr $port 1;
                      set_rcs $port 0;
     set          : Sets the item to the specified state.
                     - If the specified state has not been defined with 'new' or 'add',
                       the state data is sent.  Otherwise the data_stream associated
                       with that state is sent.
     set_data     : Use this to put data back into the serial read buffer.  Typically used
                    when you have processed only some of the serial data, and you want to
                    put the rest back because it is incomplete.  Here is an example:
                   if (my $data = said $wx200_port) {
                      my $remainder = &read_wx200($data, \%weather);
                      set_data $wx200_port $remainder if $remainder;
                   }
     said         : Returns a data record received by the port.
                     - Characters are spooled into one record until a '\n' (newline) is
                       received.  Only then does 'said $item' become valid, and it is
                       reset as soon as the said method is executed.
                     - Note: If you want to process binary serial data, specify
                             serial#_datatype=raw in the mh.ini file.  This
                             will cause said to return any data read immediately,
                             rather than buffering up data until a newline is read.
     start        : Re-starts the serial port after a stop command or after starting
                    with the port already in use.
     stop         : Stops using the serial port for this item.  That allows other programs
                    to share the port (e.g. let mh use the modem for caller ID, but turn
                    that function off when using the modem to call out).
     is_stopped   : True if the port is not active
     is_available : True if the port is not in used by another program
     set_icon : Point to the icon member you want the web interface to use.
                See the 'Customizing the web interface' section of this document for details.
     set_info : Adds additional information.  This will show up as a popup window
                on the web interface, when the mouse hovers over the command text.
                will show up as a popup window on the web interface,
                when the mouse hovers over the command text.
  Examples:
     $garage_movement = new Serial_Item('XI2');
     speak "Someone is in the garage" if state_now $garage_movement;
     $tnc_output = new Serial_Item ('CONV', 'converse', 'serial1');
     $tnc_output -> add            ('?WX?', 'wxquery');
     set $tnc_output 'converse';
     set $tnc_output "EMAIL    :userid\@computers.com Test E-Mail - $CurrentTemp deg.{0";
     my $serial_data = said $tnc_output;
     $v_tnc_close = new  Voice_Cmd("close the tnc serial port");
     stop  $tnc_output if said $v_tnc_close;
     start $tnc_output if $New_Minute and is_stopped $tnc_output
                          and is_available $tnc_output;
     $serial_tom10port = new  Serial_Item(undef, undef, 'serial_tom10');
     $serial_tom10port -> set_casesensitive;     # TOM10 needs lowercase commands

See mh/code/examples/serial_port_examples.pl for more Serial_Item examples.

Weather_Item
This object can be used to track data stored in the global %Weather array.
  Methods:
     new $type
        $type is the name of the %Weather index you want to monitor
        $type can also have a =<> comparison operator in it so
        you can make the object a true/false test.
     state        : Returns the last state, or 1/0 if $comparition and $limit were used.
     state_now    : Returns the state only when the weather data changed.
     It also inherits the Generic_Item methods.
  Examples:
   $WindSpeed = new Weather_Item 'WindSpeed';
   $WindSpeed-> tie_event('print_log "Wind speed is now at $state"');
   $freezing = new Weather_Item 'TempOutdoor < 32';
   if (state_now $fountain eq ON and state $freezing) {
     speak "Sorry fountains don't work too well when frozen";
     set $fountain OFF
   }
   $Windy = new Weather_Item 'WindSpeed > 15';
   speak "Wind is gusting at $Weather{WindSpeed} mph" if state $Windy;
                  # For current state, easiest to use it directly
   speak "Outdoor temperature is $Weather{TempOutdoor} degrees";

For examples on interface code that stores data into %Weather, see mh/code/bruce/weather_monitor.pl (uses mh/lib/Weather_wx200.pm), mh/code/public/iButton_ws_client.pl, and mh/code/public/weather_com.pl

X10_Item
This item is for controling X10 lamp modules and light switches. It is derived from Serial_Item and the strings it sends are like Serial Items, except an 'X' prefix is prepended to indicate an X10 command. The X strings are converted by one of the various X10 interfaces into the appropriate commands for that interface.
  Methods:
     new('house code[unit number]' [, 'interface'|undef [, 'option flags']])
        'house code[unit number]'  The first argument is required and is either a house 
                        code by itself or a house code and unit number.
                        Note that the X10 unit code is numbered either 1->16 or 1->9,A->G.  
                        For example device 16 in house code P could be P16 or PG
        'interface'     Optional, specifies which X10 interface to use
        'option flags'  Optional, specifies one or more module options (see below)
     state        : Returns the last state that was received or sent
     state_now    : Returns the state that was received or sent in the current pass
     level        : Returns the current brightness level of the item, 0->100
     set_receive  : Update the state and level when X10 commands are received
     set_x10_level: Recalculates state whenever state is changed
     set('state') : Sets the item to the specified state
        'on'
        'off'
        'toggle'     - toggles between on and off
        'brighten'
        'dim'
        '+##'        - increase brightness by ## points
        '-##'        - decrease brightness by ## points
        '##%'        - set brightness to ## percent
        '+##%'       - increase brightness by ## percent
        '-##%'       - decrease brightness by ## percent
        'double on'  - on some modules this sets full brightness at ramp rate
        'double off' - on some modules this sets 0 brightness immediately
        'triple on'  - same as double on, but immediate
        'triple off' - same as double off
        'status'     - requests status from a two-way capable module
        'manual'     - sends house code and unit number without a command 
 
        '&P##', 'PRESET_DIM1', 'PRESET_DIM2', 'ALL_LIGHTS_OFF', 'HAIL_REQUEST', 
        'HAIL_ACK', 'EXTENDED_CODE', 'EXTENDED_DATA', 'STATUS_ON', 'STATUS_OFF', 'Z##'
                     - these states are rarely used and provided for special cases

Note: not all states are supported by all lamp modules and X10 interfaces.

X10 items can be created in the items.mht in the following manner:

   X10I, A1, Test_light, Bedroom, cm11, preset resume=80

If a single character is used (e.g. X10_Item 'D'), commands apply to all X10_Items with that house code. The 'on' and 'off' states are translated to ALL_ON and ALL_OFF commands for that house code. For example:

   $v_test_lights = new Voice_Cmd 'All lights [on,off]';
   $test_lights   = new X10_Item 'O';
   set $test_lights $state if $state = said $v_test_lights;

The toggle and various brightness commands can be sent to a house code only item. The command will be sent to each X10_Item defined with the same house code. This might produce undesired results, particularly when changing brightness levels. See the Group item for a better way to do that.

If you are using more than one X10 interface and you want to control an X10_Item with a specific interface, use the optional interface argument. For example, if you want to control the local module on a RF Transceiver, you can tell mh to use the RF CM17 interface, like this:

    $test_light = new X10_Item('A1', 'CM17');

The various brightness commands (60%, +20, -50%) all work even on dumb modules that only support on, off, dim, and brighten. X10_Item keeps track of changes it makes to the brightness level and converts any absolute brightness settings into relative changes. Since these dumb modules typically don't have two-way capability, the item will be out of sync if changes are made locally at the switch. Also, if the module was off, it will first be turned to full on, since the older modules can not be dimmed from an off state.

After doing one or more bright/dim/on/off commands, you can query the current brightness level of a device with the level method. For example:

   if ($state = state_now $pedestal_light) {
      my $level = level $pedestal_light;
      print_log "Pedestal light state=$state, level=$level"
   }

It is much better to use one the newer (more expensive) direct dim and two-way capable modules, such as the X10 LM14A lamp module. The X10_Item supports both the newer extended data dim commands used by the LM14 and Leviton switches (64 brightness levels), and the older preset dim commands used by PCS and Switchlinc switches (32 brightness levels).

Set the 3rd X10_Item parm to specify the option flags that correspond to your lamp module or switch. Valid flags are:

   'lm14'        - for X10 LM14, uses extended data dim commands, remembers dim level when off 
   'preset'      - same as lm14 
   'preset2'     - same as lm14 and preset, except send on after direct dims, required by some Leviton switches 
   'preset3'     - same as lm14 and preset, except uses older preset dim commands, for Switchlinc and PCS 
   'resume=##'   - module resumes from off at ## percent 
   'transmitter' - special case, see X10_Transmitter

Option flags are case insensitive. Separate multiple option flags with a space.

For example:

  $test_light2 = new X10_Item('O7', undef, 'preset resume=81');
  $v_test_light2 = new Voice_Cmd("Set test light to [on,off,bright,dim,5%,10%,20%,30%,40%,50%,60%,70%,80%,90%]");
  set $test_light2 $state if $state = said $v_test_light2;

If the newer extended data dim commands are to be used, then the brightness level is converted into a &P## command and passed to the X10 interface. You can also use them directly, using &P## (## = 1->64) as shown in this example:

  $test_light1 = new X10_Item('O7', 'CM11', 'LM14');
  $v_test_light1 = new Voice_Cmd("Set test light to [on,off,bright,dim,&P3,&P10,&P30,&P40,&P50,&P60]");
  set $test_light1 $state if $state = said $v_test_light1;

Note: not all of the X10 interfaces support this command.

The older direct dim method used the two Preset Dim X10 commands. The 32 brightness levels are sent by combining a house code with one of the two Preset Dim commands, using the following table:

  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15   PRESET_DIM1
  M  N  O  P  C  D  A  B  E  F  G  H  K  L  I  J
  16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  PRESET_DIM2
  M  N  O  P  C  D  A  B  E  F  G  H  K  L  I  J

Note: not all of the X10 interfaces support this command.

Since this item is inherits from Generic_Item, you can use the set_with_timer method. For example, this event will turn on a on a warning light to 20% for 5 seconds:

   set_with_timer $watchdog_light '20%', 5 if file_unchanged $watchdog_file;

X10_Appliance
Same as X10_Item, except it has only has pre-defined states 'on' and 'off'

X10_Garage_Door;
For the Stanley Garage Door status transmitter. See mh/code/public/Danal/Garage_Door.pl

X10_IrrigationController;
For this sprinkler device: http://ourworld.compuserve.com/homepages/rciautomation/p6.htm which looks the same as the IrrMaster 4-zone sprinkler controller listed here: http://www.homecontrols.com/product.html?prodnum=HCLC4&id_hci=0920HC569027

X10_Switchlinc;
For the Switchlinc controllers:

Inherts all the functionality from X10_Item and adds the following states:

   'clear'
   'setramprate'
   'setonlevel'
   'addscenemembership'
   'deletescenemembership'
   'setsceneramprate'
   'disablex10transmit'
   'enablex10transmit'

See the <a href=``http://www.smarthome.com/manuals/2380_web.pdf>Switchlinc 2380 manual</a> for more information.

For example:

     # Just picked this device to use to send the clear
  $Office_Light_Torch->set("clear");
     # Send a command to each group member to make it listen
  $SwitchlincDisable->set("off");
     # Just picked this device item to send the command
  $Office_Light_Torch->set("disablex10transmit");

Also sets the 'preset3' X10_Item option which causes the older Preset Dim commands to be used for setting the brightness level directly.

X10_Ote;
This supports the OTE X10 themostat from Ouellet Canada.

X10_TempLinc
X10_Sensor
Do you have any of those handy little X10 MS12A battery-powered motion sensors? Here's your answer - use the X10_Sensor instead of the Serial_Item when you define it, and your house will notice when your sensor hasn't been tripped in 24 hours, allowing you to check on the batteries.

If you have an sensor that detects and sends codes for daytime and nighttime (light and dark levels), pass in a optional type MS13, Motion, or Brightness. For the id, you can use the 2 character, or 5 character X10 code. Here are some examples:

  $sensor_hall = new X10_Sensor('A4', 'sensor_hall', 'MS13');
  $work_room_motion = new X10_Sensor('CA', 'work_room_motion', 'motion');
  $work_room_bright = new X10_Sensor('CB', 'work_room_bright', 'brightness');
 .mht table examples:
   X10MS,      XA2AJ,  sensor_bathroom,       Sensors|Upstairs
   X10MS,       A4,    sensor_hall,           Sensors|Downstairs,  MS13
   X10MS,      CA,    work_room_motion,       Sensors|Motion_Sensors,      Motion
   X10MS,      CB,    work_room_brightness,   Sensors|Brighness_Sensors,   Brightness
   X10MS,      CA,    work_room_sensors,      Sensors,                  MS13    # This detects both motion and brightness

With MS13 specified, it will return states named motion,still,light, and dark. With Motion specified, it will return only the motion and still states. With Brightness specified, it will return only the light and dark states. In all cases, methods light and dark will return the current light/dark state.

Examples:

 set_with_timer  $light1 ON, 600 if $work_room_motion eq 'motion';
 speak 'It is dark downstairs' if dark $sensor_downstairs;

Without the MS13 or Brightness type, the light/dark codes will be ignored.

X10_Transmitter
Like an X10_Item, but will not be set by incoming X10 data. Simulates transmit only devices like keypads. Can be used in place of X10_Item if you have complicated code that might get into a loop because we are not ignoring incoming X10 data for transmit-only devices.

RCS_TX15
Craig is working on this one.

Socket_Item
You can use the Socket_Item object to read and/or write to TCP/IP socket ports.

Server socket ports are specified with mh.ini parms (see 'server options' in mh.ini)

  Methods:
     new('data_stream', 'state_name', 'host_port', 'name', 'host_protocol', 'datatype', 'break_char')
     add('data_stream', 'state_name')
        'host_port' must be of the form ip_address:port (when creating a client) or
        it must match a server_* parm in mh.ini (when creating a server) (e.g. server_telnet_port).
        'host_protocol' can be 'tcp' or 'udp'.  Default is 'tcp'
        'datatype'      can be 'raw', 'rawout', or 'record'.  If 'raw', the said
                        method returns data as read.  If record,
                        said only returns data when reaching a newline.
                        When writing to the port, a newline is added unless
                        datatype is 'raw' or 'rawout'.
        'break_char'    This will be added to data sent with set and, unless datatype=raw,
                        will be used to break incoming data into state_now records.
                        Default is \n\r.
        'name' is optional.  If used you can set debug to this string
        to turn on debug for just this socket data.
     is_available : Returns 1 if the socket is available, 0 if not
     set_port     : Allows you to change the server_name/port.
     state        : Returns the last state that was received or sent
     state_now    : Returns the state that was received or sent in the current pass.
     set          : Sets the item to the specified state.
                     - If the specified state has not been defined with 'new' or 'add',
                       the state data is sent.  Otherwise the data_stream associated
                       with that state is sent.
                     - If there is more than one client connected to this port, an
                       optional 2nd parm can be used to pick which client set to.
                       Values are:
                          - 'all' (send to all connected clients),
                          - ip_address (a regular expresion of the ip address(s) you want to send to
                          - client_number (which client to send to, 0 .. #).  Note this
                            can change as clients are added and dropped.
                          - socket hash name, which can be captured when reading
                            data from the port with this:
                               $client = $Socket_Ports{$port_name}{socka};
     set_echo     : Use this to control echoing of incoming characters:
                     set_echo $my_socket 0   -> Do not echo incoming characters
                     set_echo $my_socket 1   -> echo incoming characters
                     set_echo $my_socket '*' -> echo incoming characters with *
     set_expect   : Used to send a series of strings to the port, waiting for
                    a specified prompt for each string.  This is useful for
                    walking through menus.  The arguments passed to set_expect
                    are pairs of 'prompt' => 'response' strings.  See example below.
     said         : Returns a data record received by the port.
                     - Note: If you want to process binary socket data, specify
                             server_*_datatype = raw in the mh.ini file.  This
                             will cause said to return any data read immediately,
                             rather than buffering up data until a newline is read.
     said_next    : Reads and returns the next record from the socket handle
     handle       : Returns the socket handle, so you can loop on reading/writing data
                    to it directly.
     start        : Connect to the specified port.  This allows mh to act as a client,
                    rather than a server, and initiate communications with a server.
     stop         : This will drop the client or server from mh and free up the port.
     active       : True if the port is active
     active_now   : True for the mh pass that the socket first becomes active.
     inactive_now : True for the mh pass that the socket first becomes inactive.
  Examples:
                  # An example of a server socket
                  # Add this mh.ini parm:  server_speak_port = 8090
    $socket_server = new  Socket_Item(undef, undef, 'server_speak');
    if (my $data = said $socket_server) {
       print "speak_server socket data: $data\n";
       speak $data;
    }
                  # An example of a client socket
    my $lcdproc_address = 'misterhouse:13666';
    $lcdproc = new  Socket_Item(undef, undef, $lcdproc_address, 'lcdproc');
    $Vcmd_viavoice = new  main::Socket_Item(undef, undef, 'localhost:3234', 'viavoice');
                                 # This example walks the reboot menu of a router
    $router_reboot = new Voice_Cmd 'Reboot the router';
    $router_client = new Socket_Item(undef, undef, $config_parms{router_address} . ":23",
                                     'router', 'tcp', 'raw');
    set_expect $router_client (Password => $config_parms{router_password}, Number => 24,
                               Number => 4, Number => 11) if said $router_reboot;
                  # Example of sending a message to all clients
    set $telnet_server "Hi to all clients", 'all';

More examples are in mh/code/bruce/telnet.pl, mh/code/examples/socket_test*.pl, mh/code/bruce/monitor_shoutcast.pl, bruce/wxserver_server.pl, and bruce/monitor_router.pl

Text_Cmd
Use this object if you want to fire events based on text entered. Unlike the Voice_Cmd item, you can use Text_Cmd to capture arbitrary text, using a regular expression.

Like Voice_Cmd items, all text passed to the run_voice_cmd and process_external_command functions will be tested against all Text_Cmd items. All items that match will fire their state_now methods.

  Methods:
     new($re_string);
       $re_string is any valid regular expresion.  Use the () grouping to
       pick the data that will be returned with the state_now method.
     state      : Returns the text from the () match in the $re_string.
                  If there was not () grouping, it returns 1.
                  If there is more than one () grouping, the resulting
                  matches are concatonated together with | as a separator.
     state_now  : Returns the state that was received or sent in the current pass.
     Like most items, this inherits all Generic_Item methods.
  Examples:
                    # Create a widget for inputing text commands
   $Text_Input = new Generic_Item;
   &tk_entry("Text Input", $Text_Input, "tcmd1", $tcmd1);
   if ($state = state_now $Text_Input) {
      my $set_by = get_set_by $Text_Input;
      print_log "Text_Input set_by $set_by typed $state";
      run_voice_cmd($state, undef, $set_by);
   }
                    # Create commands
   $tcmd1 = new Text_Cmd('hi (a|b|c)');
   $tcmd2 = new Text_Cmd('bye *(.*)');
   $tcmd3 = new Text_Cmd('(hi.*) (bye.*)');
                    # Fire events if the commands match text input
   $tcmd1->tie_event('print_log "tcmd1 state: $state"');
   print_log "tcmd2 state=$state" if $state = state_now $tcmd2;
   print_log "tcmd3 state=$state set_by=$tcmd3->{set_by}, target=$tcmd3->{target}" if $state = state_now $tcmd3;

Timer
The Timer object can be used to run an action one or more times, at a specified interval.
  Methods:
   These methods apply to a count down timer
    new        : Used to create the object.
    set($period, $action, $cycles)
        $period is the timer period in seconds
        $action (optional) is the code (either a string or a code
                 reference) to run when the timer expires
        $cycles (optional) is how many times to repeat the timer.
                 Set to -1 to repeat forever.
    unset      : Unset the timer.  'set $my_timer 0' has the same effect.
    active     : Returns true if the timer is still running.
    inactive   : Returns true if the timer is has expired or has not been set.
    expired    : Returns true for the one pass after the timer has expired.
    run_action : Runs the timers action, even if the timer has not expired.
    hours_remaining,   hours_remaining_now
    minutes_remaining, minutes_remaining_now
    seconds_remaining, seconds_remaining_now
        These methods return the hours, minutes or seconds remaining on the timer.
        The _now methods only return the remaining time on the hour, minute,
        or second boundary.
   These methods apply to using it as a count up, or stopwatch, timer
    start      : Starts the timer
    restart    : Restarts the timer (start on an active timer does nothing)
    stop       : Stops a timer
    pause      : Pauses
    resume     : Bet you can guess :)
    query      : Returns the seconds on the timer.
  Examples:
    $timer_laundary = new  Timer;
    $v_laundary_timer = new  Voice_Cmd('Laundary timer [on,off]');
    if ($state =  said $v_laundary_timer) {
       if ($state eq ON) {
          play('rooms' => 'shop', 'file' => 'cloths_started.wav');
          set $timer_laundary 35*60, 'speak "rooms=all The laundary clothes done"', 4;
       }
       else {
          speak 'rooms=shop The laundry timer has been turned off.';
          unset $timer_laundary;
       }
    }
                   # This example uses an anonymous subroutine
    $v_test_delay = new  Voice_Cmd 'Test timer with [1,5,10] seconds';
    if ($state = said $v_test_delay) {
       print_log "Starting $state second timer test";
       my  $timer = new Timer;
       set $timer $state, sub {
          print_log "Ending $state second timer test";
       }
#      set $timer $state, "print_log 'Ending $state second timer test'";
    }
    See mh/code/bruce/timers.pl for more examples

Voice_Cmd
Use the Voice_Cmd object to create voice commands. Even without a Voice Recognition engine installed, this is useful as these commands can also be run from the Tk, web, telnet, and file interfaces.
  Methods:
     new($command, $response, $confirm, $vocabulary)
        $command can be a simple string (e.g. 'What time is it') or it can
        include a list of 'states' (e.g. 'Turn the light [on,off]').  The state
        enumeration group is a comma delimited string surrounded with [].
        In addition to one state enumeration group, you can specify any number
        of phrase enumeration groups.  These are comma delimited strings
        surrounded by {} (e.g. 'Turn the {family room,downstairs} TV [on,off]').
        Use this when you have several different ways to describe the same thing.
        $response is the text or wave file that will be played back when the
        VR engine detects this command.  If not defined, the mh.ini parm
        voice_cmd_response parm is used (default is "Ok, %HEARD%").
        You can put %STATE%, %HEARD%, or any variable in the response string and
        have it substituted/evaluated when the response is spoken.
        $confirm is either 0 or 1 (default is 0).  If set to 1, then mh will
        ask 'Confirm with a yes or a no'.  If yes or no is not heard within
        10 seconds, the command is aborted.
        $vocabulary allows you to define multiple vocabularies.  You
        can then use these functions to enable and disable the vocabularies:
             &Voice_Cmd::enablevocab($vocabulary)
             &Voice_Cmd::disablevocab($vocabulary)
        Vocabularies are enabled by default.  The default vocabulary
        is 'misterhouse'.  See mh/code/bruce/viavoice_control.pl for examples.
        This code allows you to switch between 'awake', 'asleep', and 'off' VR modes.
        NOTE:
           Currently only the viavoice VR engine (mh.ini parm voice_cmd=viavoice)
           will use the $response, $confirm, and $vocabulary_name options.
           We may be able to create a viavoice_server for windows, but that would
           probably not be free like it is on linux.  If you have a linux box
           on your network, you can have your windows mh use the linux
           viavoice_server process.
     said  : Is true for the one pass after the command was issued.
             If the command was built from a list of possible states,
             then said returns the state that matches.
     state : Returns the same thing as said, except it is valid for all passes,
             not just the pass after the command was issued.
     set_icon : Point to the icon member you want the web interface to use.
                See the 'Customizing the web interface' section of this document for details.
     set_order: Contols the order that the commands are listed in web Category list.
                The default is alphabetically by file, then by name.
  Examples:
    $v_backyard_light = new  Voice_Cmd 'Backyard Light [on,off]';
    set $backyard_light $state if $state = said $v_backyard_light;
    $v_test1 = new Voice_Cmd '{turn,set} the {living,famliy} room {light,lights} [on,off]';
    $v_test2 = new Voice_Cmd '{Please, } tell me the time';
    $v_test3 = new Voice_Cmd '{What time is it,Tell me the time}';
    $v_fan   = new Voice_Cmd 'Fan [on,off]', 'Ok, I turned the fan $v_indoor_fountain->{said}';
    $v_fan   = new Voice_Cmd 'Fan [on,off]', 'Ok, I turned the fan %STATE%';

See mh/code/examples/Voice_Cmd_enumeration.pl for more Voice_Cmd examples

In addition to the said command on a specific object, you can use &Voice_Cmd::said_this_pass to detect which command was spoken this pass and &Voice_Cmd::noise_this_pass to detect if noise was detected this pass (this function currently only works with viavoice).

  Examples:
   if (my $speak_num = &Voice_Cmd::said_this_pass) {
      my $text = &Voice_Cmd::text_by_num($speak_num);
      print_log "spoken text: $speak_num, $text";
   }
   if (my $text = &Voice_Cmd::noise_this_pass) {
      print_log "Noise detected" if $text eq 'Noise';
   }

See mh/code/bruce/lcdproc.pl for more examples.

DSC_Alarm
DSC_Alarm module supports the DSC PC5400 serial printer interface. This allows mh to be aware of events that DSC alarm systems log to their event buffers.
  Required mh.ini entries:
    DSC_Alarm_serial_port=COM1 or /dev/ttys0
    DSC_Alarm_baudrate=4800
  Multiple instances may be supported by adding instance
  numbers to the parms as in:
    DSC_Alarm:1_serial_port=COMx or /dev/ttysX
    DSC_Alarm:1_baudrate=4800
    DSC_Alarm:2_serial_port=COMy or /dev/ttysY
    DSC_Alarm:2_baudrate=4800
  Optional mh.ini entries:
    DSC_Alarm_user_40=Jane Doe
    DSC_Alarm_user_1=Bob Smith
    See the "user" method below for a description.
  Methods:
     new('alarm-name')
        Where 'alarm-name' is the prefix used in the mh.ini entry 'DSC_Alarm_serial_port=xyz'.
        The 'alarm-name' argument defaults to 'DSC_Alarm' if not specified.
     said         : Returns the last serial data received.  Valid for 1 pass only.
     Important Note:  Due to mh internals, the "said" method and the "state" method (and
     all "state" derived methods) lag each other's values by 1 pass through the user scripts.
     As such, any given script should use "said" or "state", but should NOT mix the two!
     state        : Returns last state of alarm system from following values:
                    Armed    = System is closed and armed.
                    Disarmed = System is opened.
                    Alarm    = System is alarming.
     state_now    : Same as state, but valid for 1 pass only.
     user         : User number of last code used to arm/disarm system.
                    If present, mh.ini parm DSC_Alarm_user_nn=xyz will cause "user"
                    to return string "xyz" from the parm.
     alarm_now    : True when system enters Alarm state.  Valid for 1 pass only.
     zone         : Zone number that caused Alarm. Valid only when alarm_now is true.
     mode         : Returns arming mode. Valid only when state = Armed.
                    Stay = System armed in stay mode; User pressed F1 key before arming.
                    Away = System armed in away mode; User pressed F2 key (or nothing) before arming
                    Note: Most DSC systems will not arm in "Stay" mode unless at least one zone
                          is defined as a "Stay/Away" zone.  Also, even when "Away" mode is requested
                          system will be in "Stay" mode unless a delay zone is violated during the
                          exit delay.
     Most other Generic_Item methods, such as "state_log", are valid.
  Logging:
     The internal support module for DSC_Alarm (DSC_Alarm.pm) maintains a log of
     all serial data received from the DSC PC5400 interface.  This log is placed
     in /mh.ini parm data_dir/logs/$port_name.YYYY_MM.log; for example, the log
     entries shown below would be in file '/mh/data/logs/DSC_Alarm.2000_10.log'.
     This implies a new log will be started each month.
  DSC User Codes:
     40 = Master code (can arm/disarm, change codes, any keypad function)
     41 = Supervisor code (can arm/disarm, change codes)
     42 = Supervisor code (can arm/disarm, change codes)
     01-32 = User codes (can arm/disarm, can be associated to individual wireless keys)
     33 = Duress code (can arm/disarm + sends duress code to master station)
     34 = Duress code (can arm/disarm + sends duress code to master station)
     The above information derived from PC1555 master panel; please see the installer
     manual for your particular panel for further information.
     Duress code reporting is NOT reflected via states as of December 2000.
     Coming soon...
  Examples:
    mh.ini entry of 'DSC_Alarm_serial_port=com2'
    $DSC_Alarm = new DSC_Alarm;
    if (my $log = said $DSC_Alarm) {
       print_log "Alarm system data = $log\n";
    }
    mh.ini entry of 'DSC_Alarm:2_serial_port=com5'
    $DSC_test = new DSC_Alarm('DSC_Alarm:2');
    if (my $state = state $DSC_test) {
       print_log "Alarm system state change, state = $state\n";
    }
  Examples of typical DSC alarm system event/log entries:

Mon 10/09/00 17:09:00 DSC_Alarm.pm Initialized Mon 10/09/00 17:10:16 17:10 10/09/00 System [*1] Access by User Mon 10/09/00 17:12:28 17:12 10/09/00 System Partial Closing Mon 10/09/00 17:12:28 17:12 10/09/00 System Bypass Zone 1 Mon 10/09/00 17:12:28 17:12 10/09/00 System Bypass Zone 2 Mon 10/09/00 17:12:28 17:12 10/09/00 System Bypass Zone 4 Mon 10/09/00 17:12:29 17:12 10/09/00 System Closing by User Code 40 Mon 10/09/00 17:12:29 17:12 10/09/00 System Armed in Away Mode Mon 10/09/00 17:12:45 17:12 10/09/00 System Opening by User Code 2 Mon 10/09/00 17:14:42 17:14 10/09/00 System Closing by User Code 40 Mon 10/09/00 17:14:43 17:14 10/09/00 System Armed in Away Mode Mon 10/09/00 17:14:47 17:14 10/09/00 System Opening by User Code 40 Mon 10/09/00 17:22:28 17:22 10/09/00 System [*1] Access by User Mon 10/09/00 17:33:39 DSC_Alarm.pm Initialized Tue 10/10/00 09:47:38 09:47 10/10/00 System Closing by User Code 40 Tue 10/10/00 09:47:38 09:47 10/10/00 System Armed in Away Mode Tue 10/10/00 17:48:42 17:48 10/10/00 System Opening by User Code 40 Tue 10/10/00 23:37:33 23:37 10/10/00 System Closing by User Code 40 Tue 10/10/00 23:37:33 23:37 10/10/00 System Armed in Away Mode Wed 10/11/00 07:38:11 07:38 10/11/00 System Opening by User Code 40

List of functions

add_sound
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';
    }

browser
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;

convert_k2f
convert_c2f
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

convert_direction
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});

dbm_write =item logit_dbm
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.

dbm_read
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);

dbm_search
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.

display
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.

eval_with_timer
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 object state, use the set_with_timer method.

file_default
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"));

file_size
Returns the size of a file, in bytes.

file_changed (or older name of file_change)
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);

file_unchanged
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;
    }

file_diff
Returns true if the contents of the 2 specified files are different.
  Example:
    print_log "Files are different" if file_diff($file1, $file2);

file_read
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);

file_read_dir
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;
    }

file_write
Writes data into a file. Like logit, except it writes over, rather than appends to, a file.
  Examples:
     file_write($file, $data);

file_cat
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';

file_head file_tail
Returns the first/last few lines of a file
  Examples:
     @data = file_head($file, $lines);
     $data = file_tail($file, $lines);
   If $lines is not specified, the default is 3.
   If used in a list context, a list is returned,
   otherwise a string of all the requested lines is returned.

filter_cr
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}";

logit
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");

mht_item_add
mht_item_delete
mht_item_copy
mht_item_write
These functions can be used to manipulate mht files.

play
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';

print_log
print_msg
print_speaklog
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.

round
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

run
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 object 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

run_after_delay
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";
       }
     }

run_voice_cmd
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');

SendKeys WaitForAnyWindow SetWindowText GetWindowText SetFocus EnumChildWindows sendkeys_find_window
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);
   }

respond
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}");
  }

speak
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

time_add
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";
    }

time_diff
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";

time_date_stamp
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).

time_cron
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;
    }

time_greater_than
time_greater_or_equal
time_less_than
time_less_or_equal
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");

time_between
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
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";

time_random
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)

time_random_offset
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));

List of tk widget functions

tk_button tk_mbutton
Use these functions to add a Tk button widget to the mh tk grid (tk_button) or the tk menu_bar (tk_mbutton). &tk_button will accept multiple variables, displaying them in a row in the grid.
   Usage:
    &tk_mbutton('Button Name', \&subroutine);
    &tk_button('Button1', \&sub1);
    &tk_button('Button1', \&sub1, 'Button2', \&sub2,'Button3', \&sub3);
   Examples:
    &tk_mbutton('Help', \&help);
    &tk_button('Reload(F1)', \&read_code, 'Pause (F2)', \&pause,
               ' Exit (F3) ', \&sig_handler, 'Debug(F4)',  \&toggle_debug,
               'Log(F5)', \&toggle_log);

tk_checkbutton
tk_checkbutton adds a Tk checkbutton widget to the mh tk grid. It will accept multiple variables, displaying them in a row in the grid.
   Usage:
    &tk_checkbutton('text',  \&var1);
    &tk_checkbutton('test1', \&var1, 'text22', \&var22, 'text3', \&var33);
   Examples:
    &tk_checkbutton('Debug on', \$config_parms{debug});
    &tk_checkbutton('Sleeping Parents', \$Save{sleeping_parents},
                    'Sleeping Kids', \$Save{sleeping_kids});

tk_entry
Use this function to allow for arbitrary data to be entered via the mh tk grid.
   Usage:
    &tk_entry('Entry label:', $state_based_object);
    &tk_entry('Entry label:', \$variable);
    &tk_entry('Entry label:', \$variable, 'Entry label2:, \$variable2);
   Example:
    &tk_entry('Sleep time:', \$Loop_Sleep_Time);
    &tk_entry('Test in 1', \$Save{test_input1}, 'Test in 2', \$Save{test_input2});

Note: The $variable reflects the data, as it is being entered. If you want to test on the data only after the RETURN key has been hit, use %Tk_results array. The $variable is copied to $Tk_results{'Entry label:'} only after the RETURN key has been entered.

Now you can now also use a state based object (like Generic_Item) to store/monitor/change the tk_entry text.

   Examples:
    &tk_entry('TV search', \$Save{tv_search});
    if ($state = $Tk_results{'TV search'}) {
       run qq[get_tv_info -times all -keys "$state"];
       set_watch $f_tv_file;
       undef $Tk_results{'TV search'};
    }
   $mp3_search_text =  new Generic_Item;
   $mp3_search_text -> tie_event('print_log "mp3 search text is now $state"');
   &tk_entry('mp3 Search', $mp3_search_text);

tk_label tk_mlabel
Use these functions to add a Tk label widget to the mh tk grid (tk_label) or the tk menu_bar (tk_mlabel). To avoid duplicate labels after a reload, pass a label name as a 2nd parm.
   Usage:
     &tk_mlabel(\$variable, $label);
   Example:
     &tk_mlabel(\$Save{email_flag}, 'email flag');

tk_radiobutton
Use this function to create radio buttons in the mh tk grid. If labels are not specified, the values are displayed.
   Usage:
    &tk_radiobutton('Button label:', $state_based_object, ['value1', 'value2', 'value3']);
    &tk_radiobutton('Button label:', \$variable, ['value1', 'value2', 'value3']);
    &tk_radiobutton('Button label:', \$variable, ['value1', 'value2', 'value3'],
                                                 ['label1', 'label2', 'label3']);
   Examples:
    &tk_radiobutton('Mode',  \$Save{mode}, ['normal', 'mute', 'offline']);
    &tk_radiobutton('Debug', \$config_parms{debug}, [1, 0], ['On', 'Off']);
    &tk_radiobutton('Tracking', \$config_parms{tracking_speakflag}, [0,1,2,3],
                    ['None', 'GPS', 'WX', 'All']);
    my $alarm_states = "Disarmed,Disarming,Arming,Armed,Violated,Exit Delay,Entry Delay";
    my @alarm_states = split ',', $alarm_states;
    $alarm_status    = new Generic_Item;
    &tk_radiobutton('Security Status', $alarm_status, [@alarm_states]);
    $v_alarm_status  = new Voice_Cmd "Set the alarm to [$alarm_states]";
    $v_alarm_status -> tie_items($alarm_status);
    print_log "Alarm status changed to $state" if $state = state_now $alarm_status;

trigger_set
trigger_get
You can create triggers to easily run mh code on specified events. When you create a trigger with trigger_set, mh will create or modify the code_dir/triggers.mhp file.

Use the web interface at http://localhost:8080/bin/triggers.pl (also under the ia5 MrHouse Home button) to view and update triggers.

Trigger functions are:

  trigger_set($trigger, $code, $type, $name, $replace)
    Creates or modified an existing trigger.  Only $trigger and $code are
    required.  $type defaults to OneShot (runs only once) and $name
    will default to an unique auto-generated name.  If $name is specified
    and already exists, $name will be incremented, unless $replace=1.
    Example:
      &trigger_set("time_now '$date $time - 00:02'",
                   "speak 'Something cool happens in 2 minutes'");
      &trigger_set("time_now '$Save{wakeup_time}'",
                   "speak 'Time to wake up'", "NoExpire", "Wakeup Trigger", 1);
      Another example of using triggers is in mh/code/common/tv_grid.pl
  trigger_get($name}
    Returns ($trigger, $code, $type, $triggered).
    $triggered is the epoc seconds when the trigger was
    last tirggered, 0 if it has not been triggered.
  trigger_list
    Returns a list of trigger names.
  trigger_delete($name}
  trigger_copy($name}
    Delete or copies the specified trigger.
  trigger_active($name)
  trigger_expired($name)
    Returns true if the trigger is active or expired.

Here are the valid trigger types:

  OneShot  => The trigger will run once, then changed type to Expired
  Expired  => Will be pruned from the triggers.mhp file after one week
              and archived in data_dir/triggers.expired.
  NoExpire => Runs on every event and never expires.
  Disabled => Will stay in your triggers.mph file, but will not run.

See mh/code/examples/tk_examples.pl for more tk_* examples.

List of Internet functions

get
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

get_ip_addresses
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});

html_unescape
Un-escapes ``%xx'' data back into the original characters. Use on HTML FORM data.

new_second
new_minute
new_hour
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).

net_domain_name 'address'
net_domain_name_start 'search_name', 'address'
net_domain_name_done 'search_name'
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';

net_ping
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

net_ftp
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;

net_connect_check
Returns true if connected to the Internet. If the mh.ini parm net_connect=persistent, this always returns true.

net_socket_check
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");

net_im_send
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

net_mail_send
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"';

net_mail_count
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";

net_mail_summary
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.

net_mail_read
Returns a list array of pointers to data read from an email account. This is not tested and needs to be documented.

net_mail_delete
Deletes mail from an account. A dangerous, but requested, function.

List of companion programs

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
  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
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

display
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

display_callers
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
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
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"

get_email
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
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
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
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
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
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;
  }

house
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 object defined that matches that data (e.g. X10 A1 in the above example).

ical_load
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

mhl
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
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
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
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
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
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
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
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 object 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
speak will pass the file or text to mh. It simply runs the house program.
  Example:
    speak "Boo, did I scare you?"

Authentication

A one way crypt-ed password authentication scheme can be enabled for the web and telnet.pl interfaces.

The location of the crypt-ed password is controlled with the mh.ini password_file parm. The default location is in $Pgm_Root/data/.password.

If this file does not exist, the telnet.pl and web interface will be enable for anyone and everyone. If it does exist, and the correct password is not entered, the telnet.pl and web function will not implement the requested commands.

The set_password command is used to create and check this password. If you are running the un-compiled perl and do not have Tk installed, the password will have to be passed in with the -password parm (set_password -h for more help). This can be run from a shell prompt or with the mh mh_control ``set the password'' command.

If you want to change the password, delete the password file and rerun set_password.

When prompted for UserName and Password from your web browser, you can leave UserName blank ... it is not used.

Netscape appears to allow you to re-try a password, but MS Explorer must be re-started if you want to enter a different password.

Customizing the Menu interfaces

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 .

Tellme.com currently offers free 1-800 access to their VR and TTS engines, using vxml (Voice XML) menus. You can view the mh vxml menus by pointing a XML compatible browser (e.g. IE) to this address:

   http://localhost:8080/sub?menu_vxml

When ready to try with tellme, sign up for a free developer id, which will be your extension number. I used Home0 (46630). Then you can go to my studio and enter your URL:

   http://studio.tellme.com/mystudio/mystudio.cgi
   http://your_domain/sub?menu_vxml

where your_domain is your domain name or ip address. To test, dial: 1-800-555-8965 (VXML) and enter your developer ID and Pin. When you have it working, click on the MyExtentions, enter the same URL and check the enable checkbox. Now you can dial:

   1-800-555-8355  (wait for 'tellme more') 1-yourextention

Customizing the Tk Interface

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.

Customizing the Web Interface

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 object state. Icons will be searched for in the mh/graphics directory according to the objects icon method, the object name, the object state, and the object type. For example, given this code:

    $fountain = new X10_Appliance('C3');
    set_icon $fountain 'water';

The search order for the ON state will be:

    water-on.gif
    fountain-on.gif
    x10_appliance-on.gif
    on.gif

The web Category display will try to match icons to Voice_Cmd objects using the icon method, object name, voice command text, and filename. For example, given this code from the member internet_data.pl:

 $v_set_clock = new  Voice_Cmd('Set the clock via the internet');
 set_icon $v_set_clock 'time';

The search order will be:

 Look for icons that match time
 Look for icons that match set_clock
 Look for icons that match words in 'Set the clock via the internet'
 Look for icons that match internet_data.pl

All icons in the graphics directory are compared to each of the above. The first match (by search order) is used. If there are multiple matches, the match with the longest word is used. For example, if we had icons names 'clock.gif' and 'internet.gif' (but no 'time.gif'), 'internet.gif' would be used.

In all of the above examples, .gif was used as an example, but any browser 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
    This can be used to list just the commands in the specified Category.
  /list;h_response?Category
    As above, but it also passes h_response to the RUN commands (see h_response below)
  /group
    This will return all the Group Items
  /group?$group_name
    This will return all the objects in $group_name
  /items
    This will return all the non Voice Objects (e.g. X10_Items)
  /items?object_type
    This will return all the objects of type object_type
  /widgets
    This lists all the Tk widgets.
  /widgets_label
    This returns just the label widgets
  /widgets_entry
    This returns just the entry widgets
  /widgets_radiobutton
    This returns just the radiobutton widgets
  /widgets_checkbox
    This returns just the checkbox widgets
  /speech
    This lists the most recently spoken text
  /print_log
    This lists the most recently print_log text.

If you want to generate your own html on-the-fly (like a cgi program would), instead of pointing to a .html file or 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 object. 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 object 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?$object?value
   RUN;h_response?command
   SUB;h_response?sub_name(arg1,arg2)
  $var and $object are the variable and object 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 object 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')>

Voice Recognition (VR) and Text To Speech (TTS) on windows

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.

Voice Recognition (VR) and Text To Speech (TTS) on Unix

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

Using distributed MisterHouse proxies

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 object 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.


NOTES

This section has info that doesn't fit well anywhere else. Probably be better as a FAQ.

My hope is that mh will grow into a group project. There is so many, almost unlimited possibilities in Home Automation, that no one person could hope to implement them all.

If you feel so inclined, please send me any code that you develop that you think others might be interested in, and I'll include it in the distribution.

If you would rather see it mh in your windows tray, rather in the taskbar, here is a useful utility that will let you move any program to the tray: http://www.teamcti.com/TrayIt

Another handy utility for windows 95/98 users will help you monitor how much CPU and memory mh takes. TaskInfo80, available at http://www.iarsn.com/index.html

If you are getting the message ``Out of environment space'' try adding these records:

  config.sys:   shell=command.com /e:8000 /p
  autoexec.bat: set comspec=c:\command.com

By default, declaration code like '$object = 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 object
  State_change   => Run when objects 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


KNOWN LIMITATIONS AND BUGS

General bugs/limitations

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.

Windows bugs/limitations

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.

Unix bugs/limitations

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.


AUTHORS

Bruce Winter: bruce@misterhouse.net

and a bunch of fellow home hackers


SEE ALSO

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 http://sourceforge.net/mail/?group_id=1365 You can view the archive at http://lists.sourceforge.net/pipermail/misterhouse-users/ and search it at http://lists.sourceforge.net/mailman/listinfo/misterhouse-users

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

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.


DISCLAIMER

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).