#! /usr/bin/wish4.0 -f
# x-automate
# tk-X front end for x10-amh
# 21JAN96  S. Benedict
# benedict@netcom.com

proc On { house device index root } {
	global settings
	if { $settings($index,6) == "T" } {
		if { $root == "." } {
			set root ""
		}
		if { [$root.scale$index get] != 0 } {
	    		$root.scale$index set 0
			exec x10-amh -c $house -f $device -n $device &
		} else {
			exec x10-amh -c $house -n $device &
		}
	} else {
	exec x10-amh -c $house -n $device &
	}
}

proc Off { house device index root } {
	global settings   
	exec x10-amh -c $house -f $device &
	if { $settings($index,6) == "T" } {
	    if { $root == "." } {
	         set root ""
	    }
	    $root.scale$index set 0
	}
}

proc Dim { house device slider } {
	set level [ $slider get ] 
	exec x10-amh -c $house -d $level,$device &
}

proc SetHousecode { housecode } {
    global lasthousecode
    if { $housecode==$lasthousecode } {
	return
    }
 
    set lasthousecode $housecode
    set w .housecode
    catch {destroy $w}
    toplevel $w
    wm title $w "Set Housecode"
    wm iconname $w "Set Housecode"

    frame $w.view -relief ridge -borderwidth 5
    message $w.view.text -width 18c -text "\
Resetting your base housecode will erase any memory in \
your X10 control unit.

   Are you sure you want to set the base housecode on your \
control unit to $housecode?" 

    pack $w.view.text -side left -fill both -expand 1
    pack $w.view -side top -fill both -expand 1 -padx 10 -pady 10

    frame $w.buttons
    button $w.buttons.no -text No -command "destroy $w"
    pack $w.buttons.no -side left -expand y -fill y -padx 15 -pady 10
    button $w.buttons.yes -text Yes -command \
	"exec echo y | x10-amh -z $housecode; destroy $w"
    pack $w.buttons.yes -side left -expand y -fill y -padx 15 -pady 10
    pack $w.buttons -side bottom -fill x -expand y

}
	
proc Utility { command } {

    set w .cp290
    catch {destroy $w}
    toplevel $w
    wm title $w "CP-290 Utility"
    wm iconname $w "CP290 Utility"

    frame $w.view -relief ridge -borderwidth 5
    message $w.view.text -width 18c -text [eval exec $command]

    pack $w.view.text -side left -fill both -expand 1
    pack $w.view -side top -fill both -expand 1 -padx 10 -pady 10

    frame $w.buttons
    button $w.buttons.dismiss -text Dismiss -command "destroy $w"
    pack $w.buttons.dismiss -side left -expand y -fill y -padx 15 -pady 10
    pack $w.buttons -side bottom -fill x -expand y

}    

proc ListCP-290 {} {

    set w .list
    catch {destroy $w}
    toplevel $w
    wm title $w "Please Wait - Querying CP-290..."
    wm iconname $w "List CP-290"
    $w configure -cursor watch
    
    frame $w.view
    text $w.view.text -width 48 -height 24 \
	-takefocus 0 -yscrollcommand "$w.view.scrollbar set" \
	-relief sunken -borderwidth 2 -state disabled \
        -wrap word

    pack $w.view.text -side left -fill both -expand 1 -pady 10
    scrollbar $w.view.scrollbar -command "$w.view.text yview"
    pack $w.view.scrollbar -side left -fill y -padx 3 -pady 10
    pack $w.view -side top -fill both -expand 1 -padx 10
    
    frame $w.buttons
    button $w.buttons.dismiss -text Dismiss -command "destroy $w"
    pack $w.buttons.dismiss -side left -expand y -fill y -padx 15 -pady 10
    pack $w.buttons -side bottom -fill x -expand y
    tkwait visibility $w
    exec x10-amh -l >.x10dump 2> /dev/null
    $w configure -cursor arrow
    wm title $w "List CP-290"

    $w.view.text configure -state normal
    $w.view.text insert 1.0 [exec cat .x10dump]
    $w.view.text configure -state disabled
}    

proc Sound { action args } {
   global AudioLine 
   if {$AudioLine==""} { return }
   if { ($action=="on") || ($action=="off") } {
   	append phrase $args "-" $action
   } else {
	set level [ $action get ]
	if {$AudioLine=="speak \"%sound\""} {
	    append phrase $args " dim " $level
	} else {
	    append phrase $args "-dim"
	}
   }
   regsub %sound $AudioLine $phrase SoundCommand
   eval exec $SoundCommand
}

proc ReadSetup { filename } {
global settings floors AudioLine NumOfUnits Floorlist
	
    set setupfile [open $filename r]
    set i 1
    while {[gets $setupfile line] >= 0} {
	    if {[string first # $line] == -1} {
	        if {[string first Audio: $line] != -1} {
		    set AudioLine [string trimleft $line "Audio: "]
	        } elseif {[string first Floors: $line] != -1} {
    		    set Floorlist [string trimleft $line "Floors: "]
		    set floors [ split [string trimleft $line "Floors: "] , ] 		
	        } elseif {[string length $line] > 0} {
	            set fields [ split $line , ]
	            foreach s { 0 1 2 3 4 5 6 7 8 } {
		    set settings($i,$s) [lindex $fields $s]
	        }
	        incr i
	        }
	    }	    	
    }
    set NumOfUnits [expr $i-1]
    close $setupfile

    # add pixmaps

    for {set i 1} { $i<=$NumOfUnits } { incr i} {
        image create photo $settings($i,8)-on -file $settings($i,8)on.gif
        image create photo $settings($i,8)s-on -file $settings($i,8)ons.gif
        image create photo $settings($i,8)-off -file $settings($i,8)off.gif
        image create photo $settings($i,8)s-off -file $settings($i,8)offs.gif
        global settings($i,8)-on settings($i,8)s-on \
	       settings($i,8)-off settings($i,8)s-off
    }

}

proc ReadSchedule { filename } {
    global schedule NumOfEvents prefix
    
    set schedulefile [open $filename r]
    set i 0
    while {[gets $schedulefile line] >= 0} {
        if {[string first event $line] != -1} {
	    foreach s { 0 1 2 3 4 5 6 7 } {
	        gets $schedulefile line
		# in case there is no dim value
		if { $line != "\}" } {
		    set line [string trim $line " "]
		    set line [string trimleft $line [lindex $prefix $s]]
    		    set schedule($i,$s) [string trim $line " "]
		} else {
		    set schedule($i,$s) ""
		}
	    }
	    incr i
        }
    }	    	
    set NumOfEvents [expr $i-1]
    close $schedulefile
}

proc WriteSetup { filename } {
    global settings floors AudioLine NumOfUnits
	
    set setupfile [open $filename w]
    set text \
"# setup file for x-automate - X interface for Aaaron Hightower's x10-amh
# Jan. 21, 1996 - Stew Benedict - benedict@netcom.com

# audio command
# if no audio then leave blank
# could also use 'cat %sound > /dev/audio'
"
    puts $setupfile $text
    set buff ""
    append buff "Audio: " $AudioLine
    puts $setupfile $buff

    set text "
# next describe the floors and floorplan files
"
    puts $setupfile $text

    set floorlist [join $floors ","]
    set buff ""
    append buff "Floors: " $floorlist
    puts $setupfile $buff
    unset buff
    
    set text "
# now the devices, format:
# floor,x,y,housecode,channel,description,dimmable?,sound,icon-prefix
"
    puts $setupfile $text
                                                          
    for {set i 1} { $i<=$NumOfUnits } { incr i } {
	foreach s { 0 1 2 3 4 5 6 7 8 } {
            lappend buff $settings($i,$s)
	}	    	 
	set buff2 [join $buff ","]
        puts $setupfile $buff2
	unset buff
    }
    close $setupfile
}

proc WriteSchedule { filename } {
    global schedule NumOfEvents prefix

    set spacing { "    " "    " " " "      " "    " "      " "  " "  " } 
    set schedulefile [open $filename w]
    for { set i 0} { $i <=$NumOfEvents } { incr i } {
    	puts $schedulefile "event \{"
	    foreach s { 0 1 2 3 4 5 6 7 } {
		if { $schedule($i,$s) != "" } {
		    append buff "  " [lindex $prefix $s] \
			[lindex $spacing $s] $schedule($i,$s)
		    puts $schedulefile $buff
		    unset buff
	        } 
	    }
	puts $schedulefile "\}\n"
    }
    close $schedulefile
}

proc EditSetup { } {
    global file settings NumOfUnits AudioLine Floorlist 
    set orgNumOfUnits $NumOfUnits
    
    set w .editsetup 
    catch {destroy $w}
    toplevel $w
    bind $w <Key-Escape> "UpdateSetup; destroy $w"
    wm iconbitmap $w @x-automate.xbm
    wm title $w "Edit Setup"
    wm iconname $w "Edit Setup"

    frame $w.wrapper

    scrollbar $w.scroll \
         -command "$w.canvas yview" \
         -relief raised

    canvas $w.canvas \
         -yscrollcommand "$w.scroll set" \
         -width 880 -height 400 \
         -scrollregion {0 0 880 1200} \
         -confine true \
         -yscrollincrement 25 

    set widths { 10 10 10 10 10 15 10 15 10 }
    
    frame $w.entries
    pack $w.entries
    for {set i 0} { $i<=8 } { incr i} {
        frame $w.entries.$i
        pack $w.entries.$i -side left -fill x
    }
    foreach s {0 1 2 3 4 5 6 7 8} {
	for {set i 1} {$i<=$NumOfUnits} {incr i} {   
	    entry $w.entries.$s.entry$i -width [lindex $widths $s] \
	        -textvariable settings($i,$s)
	    pack $w.entries.$s.entry$i -padx 5 -pady 2 -fill x
	}
    }

    frame $w.menu -relief raised -bd 2
    button $w.menu.help -text "Help" -height 1 -relief flat \
         -command { Help Setup }
    pack $w.menu.help -side right

    frame $w.top 
    label $w.top.label1 -text Audio:
    entry $w.top.entry1 -textvariable AudioLine -width 50
    label $w.top.label2 -text Floors:
    entry $w.top.entry2 -textvariable Floorlist -width 50
    pack $w.top.label1 $w.top.entry1 $w.top.label2 $w.top.entry2 \
	 -side left -pady 15 -expand y

    frame $w.title -relief ridge -borderwidth 4
    
    set titles { Floor "X Location" "Y Location" Housecode Channel \
	Description Dimmable? Audio Icon-Prefix }
    for {set i 0} { $i<=8 } { incr i} {
    	label $w.title.$i -text [lindex $titles $i]\
            -width [lindex $widths $i]  
    	pack $w.title.$i -pady 10 -expand y -side left -padx 5
    }
    frame $w.title.filler -width 20
    pack $w.title.filler -side left
    
    frame $w.buttons -relief ridge -borderwidth 4

    button $w.buttons.add -text "Add Device" -command \
	{ incr NumOfUnits; set i $NumOfUnits
          set widths { 10 10 10 10 10 15 10 15 10 }
          set w .editsetup
	  foreach s {0 1 2 3 4 5 6 7 8} {
              set settings($i,$s) ""
              entry $w.entries.$s.entry$i -width \
	          [lindex $widths $s] -textvariable settings($i,$s)
              pack $w.entries.$s.entry$i -padx 5 -pady 2 -fill x
	  }
	}
    button $w.buttons.insert -text "Insert Device" -command \
	{ set w .editsetup
          set widths { 10 10 10 10 10 15 10 15 10 }
	  set rowname [winfo name [focus -lastfor $w]]	
	  if {[string first entry $rowname] != -1} {  
              set row [string trimleft $rowname entry] 
              set NumOfUnits [expr $NumOfUnits+1]
	      for { set i $NumOfUnits } { $i>$row } { set i [expr $i-1] } {  
                  foreach s { 0 1 2 3 4 5 6 7 8} {
		      set previous [expr $i-1]
		      set settings($i,$s) $settings($previous,$s)
                  }
	      }
              set i $NumOfUnits
	      foreach s { 0 1 2 3 4 5 6 7 8 } {
	          set settings($row,$s) ""
                  entry $w.entries.$s.entry$i -width \
	               [lindex $widths $s] -textvariable settings($i,$s)
                  pack $w.entries.$s.entry$i -padx 5 -pady 2 -fill x
	      }
          }  
	}
    button $w.buttons.delete -text "Delete Device" -command \
	{ set w .editsetup
          set widths { 10 10 10 10 10 15 10 15 10 }
	  set rowname [winfo name [focus -lastfor $w]]	
	  if {[string first entry $rowname] != -1} {  
              set row [string trimleft $rowname entry]  
	      for { set i $row } { $i<$NumOfUnits } { incr i } {  
                  foreach s { 0 1 2 3 4 5 6 7 8 } {
		      set next [expr $i+1]
		      set settings($i,$s) $settings($next,$s)
                  }
	      }
	      foreach s { 0 1 2 3 4 5 6 7 8 } {
	          destroy $w.entries.$s.entry$NumOfUnits
	      }
          set NumOfUnits [expr $NumOfUnits-1]
          }  
          DisplayControls
	}

    button $w.buttons.done -text Done -command " destroy $w "
    pack $w.buttons.add $w.buttons.insert $w.buttons.delete $w.buttons.done \
       -side left -expand y -fill y -padx 15 -pady 15
       
    $w.canvas create window 0 0 -window $w.entries -anchor nw
    pack $w.menu -side top -fill x
    pack $w.top $w.title -fill x -expand y
    pack $w.canvas $w.scroll -in $w.wrapper -side left -fill y
    pack $w.wrapper $w.buttons -side top -fill x -expand y 
    
}

proc EditSchedule {} {
    global schedule NumOfEvents prefix 

    set w .editschedule
    catch {destroy $w}
    toplevel $w
    bind $w <Key-Escape> "UpdateSchedule; destroy $w"
    wm iconbitmap $w @x-automate.xbm
    wm title $w "Edit Schedule"
    wm iconname $w "Edit Schedule"

    frame $w.wrapper

    scrollbar $w.scroll \
         -command "$w.canvas yview" \
         -relief raised

    canvas $w.canvas \
         -yscrollcommand "$w.scroll set" \
         -width 750 -height 400 \
         -scrollregion {0 0 750 3600} \
         -confine true \
         -yscrollincrement 25 
    
    set widths { 10 15 10 10 10 10 10 10 }

    frame $w.entries
   
    for {set i 0} { $i<=7 } { incr i} {
        frame $w.entries.$i
        pack $w.entries.$i -side left -fill x
    }
    foreach s {0 1 2 3 4 5 6 7 } {
	for {set i 0} {$i<=$NumOfEvents} {incr i} {   
	    entry $w.entries.$s.entry$i -width [lindex $widths $s] \
	        -textvariable schedule($i,$s)
	    pack $w.entries.$s.entry$i -padx 5 -pady 2 -fill x
        }
    }

    
    frame $w.menu -relief raised -bd 2
    button $w.menu.help -text "Help" -height 1 -relief flat \
         -command { Help Schedule }
    pack $w.menu.help -side right
    
    frame $w.title -relief ridge -borderwidth 4
    
    for {set i 0} { $i<=7 } { incr i} {
    	label $w.title.$i -text [lindex $prefix $i]\
            -width [lindex $widths $i]  
    	pack $w.title.$i -pady 10 -expand y -side left -padx 5 
    }    
    frame $w.title.filler -width 20
    pack $w.title.filler -side left
    
    frame $w.buttons -relief ridge -borderwidth 4
    button $w.buttons.add -text "Add Event" -command \
	{ incr NumOfEvents; set i $NumOfEvents
          set widths { 10 15 10 10 10 10 10 10 }
          set w .editschedule
	  foreach s {0 1 2 3 4 5 6 7 } {
              set schedule($i,$s) ""
              entry $w.entries.$s.entry$i -width \
	          [lindex $widths $s] -textvariable schedule($i,$s)
              pack $w.entries.$s.entry$i -padx 5 -pady 2 -fill x
	  }
	}
    button $w.buttons.insert -text "Insert Event" -command \
	{ set w .editschedule
          set widths { 10 15 10 10 10 10 10 10 }
	  set rowname [winfo name [focus -lastfor $w]]	
	  if {[string first entry $rowname] != -1} {  
              set row [string trimleft $rowname entry] 
              set NumOfEvents [expr $NumOfEvents+1]
	      for { set i $NumOfEvents } { $i>$row } { set i [expr $i-1] } {  
                  foreach s { 0 1 2 3 4 5 6 7 } {
		      set previous [expr $i-1]
		      set schedule($i,$s) $schedule($previous,$s)
                  }
	      }
              set i $NumOfEvents
	      foreach s { 0 1 2 3 4 5 6 7 } {
	          set schedule($row,$s) ""
                  entry $w.entries.$s.entry$i -width \
	               [lindex $widths $s] -textvariable schedule($i,$s)
                  pack $w.entries.$s.entry$i -padx 5 -pady 2 -fill x
	      }
          }  
	}
    button $w.buttons.delete -text "Delete Event" -command \
	{ set w .editschedule
          set widths { 10 15 10 10 10 10 10 10 }
	  set rowname [winfo name [focus -lastfor $w]]	
	  if {[string first entry $rowname] != -1} {  
              set row [string trimleft $rowname entry]  
	      for { set i $row } { $i<$NumOfEvents } { incr i } {  
                  foreach s { 0 1 2 3 4 5 6 7 } {
		      set next [expr $i+1]
		      set schedule($i,$s) $schedule($next,$s)
                  }
	      }
	      foreach s { 0 1 2 3 4 5 6 7 } {
	          destroy $w.entries.$s.entry$NumOfEvents
	      }
          set NumOfEvents [expr $NumOfEvents-1]
          }  
	}

    button $w.buttons.done -text Done -command "destroy $w"
    pack $w.buttons.add $w.buttons.insert $w.buttons.delete $w.buttons.done \
       -side left -expand y -fill y -padx 15 -pady 15
       
    $w.canvas create window 0 0 -window $w.entries -anchor nw
    pack $w.menu -side top -fill x
    pack $w.title -fill x -expand y
    pack $w.canvas $w.scroll -in $w.wrapper -side left -fill y
    pack $w.wrapper $w.buttons -side top -fill x -expand y 
     
}

proc DisplayControls { } {
    global settings NumOfUnits
    set w .

    #destroy any old buttons, sliders etc.
    
    eval destroy [winfo children .top.right]
    eval destroy [winfo children .top.center]
    eval destroy [winfo children .top.left]
    
    if {$NumOfUnits==0} { return }

    for {set i 1} { $i<=$NumOfUnits } { incr i} {
       set house $settings($i,3)
       set device $settings($i,4)
       if {[string first ~ $settings($i,7)]==-1} { 
           set audio 7
       } else {
	   set audio 5
       }
       set sound $settings($i,$audio)
       set w .top.right
 
       button $w.light$i-on -image $settings($i,8)-on -command \
        " On $house $device $i .top.center; Sound on $sound"
       set w .top.center
       label $w.label$i -text $settings($i,5) 
       # check if unit is dimmable - if so then setup scale for dimming
       # bind the scale widget with no command but dim on release to
       # prevent overwhelming the CP290 with commands
       if { $settings($i,6) == "T" } {
	   scale $w.scale$i -showvalue 0 -orient hor \
	      -from 15 -to 0 -width 6 
           bind $w.scale$i <ButtonRelease> \
	      " Dim $house $device $w.scale$i ; Sound $w.scale$i $sound "
       }
       set w .top.left
       button $w.light$i-off -image $settings($i,8)-off -command \
	   " Off $house $device $i .top.center; Sound off $sound "
    }

    for {set i 1} { $i<=$NumOfUnits } { incr i} {
        pack .top.left.light$i-off -side top
        if { $settings($i,6) == "T" } {
            pack .top.center.label$i -side top -pady 2  
            pack .top.center.scale$i -side top 
        } else {
            pack .top.center.label$i -side top -pady 11 
        }      
        pack .top.right.light$i-on -side top           
    }

}

proc MakeFloorImages { floors } {
    global NumOfUnits settings
    set w .floorplan
    catch {destroy $w}
    toplevel $w
    wm iconbitmap $w @x-automate.xbm
    wm title $w "Floorplans"
    wm iconname $w "Floorplans"
    frame $w.menu
    button $w.menu.help -text "Help" -height 1 -relief flat \
         -command { Help Floorplan }
    pack $w.menu.help -side right
   
    frame $w.view -relief sunken -borderwidth 2
    frame $w.buttons 
    button $w.buttons.cancel -text Cancel -command "destroy $w"
    pack $w.buttons.cancel -side left -padx 15 -pady 4 -expand y

set i 0
while {$i<[llength $floors]} {
    canvas $w.view.floor$i -closeenough 5
    set imagename [image create bitmap -file [lindex $floors $i]] 
    set x [image width $imagename]
    set y [image height $imagename]
    set floor [string trimright [lindex $floors $i] ".xbm"]
    $w.view.floor$i create image 0 0 -image $imagename\
         -anchor nw 
    $w.view.floor$i configure -width $x -height $y
    for {set j 1} { $j<=$NumOfUnits } { incr j} {
        if { $settings($j,0) == [expr $i+1] } {
	    frame $w.view.$j -relief ridge -borderwidth 3 
            frame $w.view.$j.top
	    frame $w.view.$j.bottom
            set house $settings($j,3)
            set device $settings($j,4)
            if {[string first ~ $settings($j,7)]==-1} { 
	         set audio 7
	    } else {
	         set audio 5
	    }
            set sound $settings($j,$audio)
            button $w.light$j-on -image $settings($j,8)s-on -command \
                " On $house $device $j $w; Sound on $sound"
            label $w.label$j -text $settings($j,5) -font 6x10 
            # check if unit is dimmable - if so then setup scale for dimming
            # bind the scale widget with no command but dim on release to
            # prevent overwhelming the CP290 with commands
            if { $settings($j,6) == "T" } {
                scale $w.scale$j -showvalue 0 -orient hor -from 15 -to 0 \
	           -width 6 -length 56
                bind $w.scale$j <ButtonRelease> \
	           " Dim $house $device $w.scale$j ; Sound $w.scale$j $sound "
            }
            button $w.light$j-off -image $settings($j,8)s-off -command \
	        " Off $house $device $j $w; Sound off $sound "
            pack $w.light$j-off $w.light$j-on \
	        -in $w.view.$j.top -side left
	    if { $settings($j,6) == "T" } {
	         pack $w.scale$j \
		    -in $w.view.$j.bottom -side top
	    }
	    pack $w.label$j -in $w.view.$j.bottom -side top
	    pack $w.view.$j.top $w.view.$j.bottom \
	        -in $w.view.$j -side top
	    $w.view.floor$i create window \
		 $settings($j,1) $settings($j,2) \
		     -window $w.view.$j
            }	     
    }
    button $w.buttons.floor$i -text "$floor" -command \
         "ClearFrame $w.view ; pack $w.view.floor$i -side left \
         -fill both -expand 1"
    pack $w.buttons.floor$i -side left -padx 15 -pady 4 -expand y
    bind $w.view.floor$i <1> "itemStartDrag $w.view.floor$i %x %y"
    bind $w.view.floor$i <B1-Motion> "itemDrag $w.view.floor$i %x %y" 
    incr i
    }
    
    $w.buttons.floor0 invoke
    pack $w.menu -side top -fill x
    pack $w.view -side top -fill x \
        -expand y -padx 5
    pack $w.buttons -side top -fill x -expand y -pady 2 -padx 5

}

# Utility procedures to support dragging of items.

proc itemStartDrag {c x y} {
    global lastX lastY
    set lastX [$c canvasx $x]
    set lastY [$c canvasy $y]
}

proc itemDrag {c x y} {
    global lastX lastY settings
    if {[$c type current]=="window"} {
    	set x [$c canvasx $x]
    	set y [$c canvasy $y]
    	set newcoords [split [$c coords current]]
    	set windowname [$c itemcget current -window]
    	set windowparent [winfo parent $windowname]
    	set index [string trimleft $windowname $windowparent]
    	set settings($index,1) [lindex $newcoords 0]
    	set settings($index,2) [lindex $newcoords 1]
    	$c move current [expr $x-$lastX] [expr $y-$lastY]
    	set lastX $x
    	set lastY $y
    }
}

proc ClearFrame { frame } {
    pack forget [pack slaves $frame]
}

proc About {} {

    set text "\
x-automate, by Stew Benedict (benedict@netcom.com), copyright 1996.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

                      Thanks to Aaron Hightower for x10-amh. 
                  Also thanks to Mark Crimmins, author of Dialer 
                           in the TKGoodstuff distribution 
               (the basis of the spreadsheet type editing screens) 
                 and Mario Jorge Silva for the file selector code.

                   Thanks also to John Ousterhout, the author
                   of Tcl/Tk and Linus Torvalds and the whole 
                Linux community for a GREAT operating system!

                   Last but not least, thanks to my girlfriend
                 DiAnne and my daughter Emma, for putting up
                      with my compulsive hacking/tinkering.
"

    set w .about
    catch {destroy $w}
    toplevel $w
    wm iconbitmap $w @x-automate.xbm
    wm title $w "About x-automate"
    wm iconname $w "About x-automate"

    label $w.title -text "x-automate" 
    pack $w.title -pady 10 -fill x -expand y

    frame $w.view -relief groove -borderwidth 5
    message $w.view.text -width 18c -text $text

    pack $w.view.text -side left -fill both -expand 1
    pack $w.view -side top -fill both -expand 1 -padx 10

    frame $w.buttons
    button $w.buttons.dismiss -text Dismiss -command "destroy $w"
    pack $w.buttons.dismiss -side left -expand y -fill y -padx 15 -pady 15
    pack $w.buttons -side bottom -fill x -expand y

}    

proc Help { mode } {

switch $mode {

Setup { set text \
"       
       This screen is for editing your loaded setup.  You can change the
    number and callouts for the your X10 devices, as well as the audio
    settings and the floorplan graphics.  No checking is done for valid
    entries, so don't be suprised if you enter something wrong, or leave
    fields blank and you don't get good results.

    Add Device:      Adds row to end of device table
    Insert Device:   Inserts row at current row of device table
    Delete Device:   Deletes current row of device table, immediately
	             updates control panel.
    Done:            Destroys device table window and updates control
	             panel.
"
}

Schedule { set text \
" 
       This screen is for editing schedule files to upload to the
    CP-290.  I would not recommend leaving blank rows when the 
    schedule is uploaded.

    Add Event:      Adds row to end of event table
    Insert Event:   Inserts row at current row of event table
    Delete Event:   Deletes current row of event table.
    Done:            Destroys device table window and updates control
	             panel.
"
}

Floorplan { set text \
"
       This screen display a graphical reprentation of your facility
    with your devices located about the flooplan.  Buttons are
    available to select the different floors.  You can move the devices
    by clicking on the edge of them and dragging.  This will update
    the x,y coordinates of the setup table.
"
}
}
    set w .help
    catch {destroy $w}
    toplevel $w
    wm iconbitmap $w @x-automate.xbm
    wm title $w "$mode Help"
    wm iconname $w "$mode Help"

    frame $w.view
    text $w.view.text -width 80 -height 15 \
	-takefocus 0 \
	-relief sunken -borderwidth 2 -state disabled \
        -wrap word

    pack $w.view.text -side left -fill both -expand 1
    pack $w.view -side top -fill both -expand 1 -padx 10
    $w.view.text configure -state normal
    $w.view.text insert end "$text"
    $w.view.text configure -state disabled

    frame $w.buttons
    button $w.buttons.dismiss -text Dismiss -command "destroy $w"
    pack $w.buttons.dismiss -side left -expand y -fill y -padx 15 -pady 15
    pack $w.buttons -side bottom -fill x -expand y
}

proc XAutomateHelp {} {

    set text \
"       x-automate is a GUI interface for Aaron Hightower's x10-amh.
   With this program you can attach icons to your various X10 devices and
   see them displayed on a toolbar as well as a floorplan of your own. You
   can also read, write and edit programs to download to the CP-290
   controller.

 	x-automate initially reads the file .x10rc in your home 
   directory.  The format of this file is as follows (I've included a
   sample system.x10rc in this distribution):
   
   Blank lines and those prefixed with '#' are ignored.

   First line defines the audio tool:

		Audio: cat \"%sound.au\" > /dev/audio

	This will play an audio file when pushing the icon for the
	device.  Alternately, you can leave this blank for no audio,
	or put: speak \"%sound\" for the 'speak' text-to-speech program.

   I've tried this with the cat technique, play from sox, and vplay.

   For vplay you need to do this to suppress the stdout messages:

	       Audio: vplay \"%sound.voc\" 2> /dev/null

   play from sox is the best of these, as it doesn't send anything to
   stdout, and it doesn't care about the file format.

	       Audio: play \"%sound.wav\" 

   For any of these techniques using audio files, you'll need three
   files: 1 each for on, off, and dim, with the filename xxxx-on.xxx,
   xxxx-off.xxx xxx-dim.xxx. I've included some samples just for
   demonstration purposes.

   The next line defines the floorplan files:

               Floors: floor1.xbm,floor2.xbm,floor3.xbm

     I would recommend creating the floorplans with xfig with the -e xbm
  option, and then exporting them to a bitmap format file.  Keep in mind
  your screen size when creating this file if you want the whole floor
  to be visible on the screen.
  
  The remaining lines outline the devices in your home.  The format is:

  floor,x,y,house code,channel,description,dimmable,audio-prefix,icon-prefix
		
		1,200,300,A,1,dining room,n,~,ceiling,
		1,400,500,A,2,porch,d,~,ceiling
		2,300,200,A,3,hall,d,~,ceiling,
		2,100,100,A,4,stereo,d,~,ceiling
		3,600,800,A,5,3rd floor stairs,d,~,ceiling

	Definitions:

	Floor: The floor of your home for the floorplan screens
	x: The x coordinate on the floorplan
	y: The y coordinate on the floorplan
	House Code: The house code assigned to the device. (A-P)
	Channel: The numerical address assigned to the device (1-16)
        Description: The text description for the toolbar.
	Dimmable: T for true, F for false - determines whether a
	       dim control will be present.
	Audio-prefix: A ~ means to repeat the text description.  
	       This is for the 'speak' program I use for 
	       text-to-speech conversion.  Otherwise you can 
	       specify an audio file for the assigned audio 
	       program in the Audio: line of the .x10rc file.
	       This will be concencated with 'on' or 'off' for 
	       the appropriate pushbutton. 
	Icon-prefix: The first part of the icon name.  This will be
		     concencated with 'on' or 'off' for the appropriate
		     pushbutton. 

	I've include an editing table to simplify the editing of the 
 	setup files. If you load a new setup file, or insert or delete
	rows in the setup editing screen, the 'remote control' display
        will be updated with your changes.  There is no error checking
        on this data, so if you enter bad info, you'll probably break
        the program.

   Menu:

	The menu has the following commands:

	File:

           The file menu has New, Load, Save, and Save As for two
	types of files - the Setup File (same as the .x10rc file),
	and the Schedule File (for downloading to the CP-290), You 
        should create an x10 directory under your home as this is 
        the default location for these files.  The setup files have 
        the suffix '.set', the schedule files '.sch'. This menu 
	also has a Display Floorplan item to view your facility's
	layout with the devices. It also has an Exit item to exit 
	the program.

	Edit:

	   The edit menu has options to edit the setup and schedule
	currently loaded.  These will be displayed in a tabular form
	where you can edit each field and save the changes. 

	Utility:

	   The utility menu has options for :

		Query CP-290 - Show Day/Time for the CP-290
		Set CP-290 - Set the CP-290's Day/Time to the CPU Day/Time
		Change Housecode - Set the default housecode for the CP-290 
		CP-290 Self Test - Run the CP-290 self test and report the
                   results. (I have to admit - I've never gotten good 
                   results from this, even from the command line)
		List CP-290 Contents - List the schedule in the CP-290's 
                   memory. This is dumped to the file .x10dump if you
                   want to save the data.

	Panic:

	   This menu implements the 'All Units Off' and 'All Units On' 
        functions for the default house code.

	Help:

	   The help menu has an 'About' which gives the GNU copyleft
	statement and thanks to those whose previous work contributed
	to this effort.  It also has x-automate Help which displays
	this text.

   Icons:  I've included an number of icons with the package.  These
	are 32x32 GIFs, one for the on and off conditions of each 
        device.  There is also a duplicate set of 20x20 GIFs which 
        occupy less space on the floorplan.  These have an 's' on the
        end of the filename.  Unfortunately, since the X10 system does
        not let you poll devices for their state, it's up to you to 
        know whether the device is already on or off.  If you create 
        new GIFs for icons, I'd welcome getting them to include in the 
        package, as mine leave a bit to be desired (but they're 
        functional).

   Floorplans:  When you activate 'Display Floorplan' on the File menu,
	you will be presented with a graphical representaion of your
	facility with the appropriate device controls located in frames
	on the floorplan.  These frames can be moved by clicking and
	dragging with the mouse pointer near their perimeter.  This will
	update the x,y coordinates in the setup, which can then be saved.
	There will be buttons available to display the various floors of
	your facility, and the controls will be present on each floor
	according to the floor defined in the setup file. If you change
        settings in the setup file, the floorplan will not be updated.
        You need to close and reopen your floorplan.

   Schedules:  The schedule files follow the format outlined in
	Aaron Hightower's x10-amh package.  I've include an editing
	table screen to simplify the editing of them.  The file format 
        is as follows:

			event {
 			  devmap    2
  			  daymap    1,2,3,4,5,6,7
  			  housecode a
 			  mode      today
  			  minute    39
  			  hour      23
  			  function  dim
  			  dimlevel  5
			}

			event {
  			  devmap    2
  			  daymap    1,2,3,4,5,6,7
  			  housecode a
  			  mode      today
  			  minute    0
  			  hour      5
  			  function  off
			}

    That's about it, I hope you find this useful in some way.
    Feedback is always welcome.

    Copyright 1996 - Stew Benedict, benedict@netcom.com

"
    set w .help
    catch {destroy $w}
    toplevel $w
    wm iconbitmap $w @x-automate.xbm
    wm title $w "x-automate Help"
    wm iconname $w "x-automate Help"

    label $w.title -text "x-automate Help" 
    pack $w.title -pady 10 -fill x -expand y

    frame $w.view
    text $w.view.text -width 80 -height 20 \
	-takefocus 0 -yscrollcommand "$w.view.scrollbar set" \
	-relief sunken -borderwidth 2 -state disabled \
        -wrap word

    pack $w.view.text -side left -fill both -expand 1
    scrollbar $w.view.scrollbar -command "$w.view.text yview"
    pack $w.view.scrollbar -side left -fill y -padx 3
    pack $w.view -side top -fill both -expand 1 -padx 10
    $w.view.text configure -state normal
    $w.view.text insert end "$text"
    $w.view.text configure -state disabled

    frame $w.buttons
    button $w.buttons.dismiss -text Dismiss -command "destroy $w"
    pack $w.buttons.dismiss -side left -expand y -fill y -padx 15 -pady 15
    pack $w.buttons -side bottom -fill x -expand y

}    

proc Save { wildcard type } {
    global file setupfile schedulefile
    if ![select $wildcard "Save File..." f "$type File name:" ] {
	return
    }
    if [file exists $f] {
	set response [tk_dialog .overwrite "File Exists" \
	    "File Exists...OverWrite?" question 0 Yes No Cancel]
	
        switch $response {
	    
	    1 { Save $wildcard $type }
	    
	    2 { return }
	}
    }
    if [catch "exec touch $f"] {
	tkerror "Error: can't create $f."
	return
    }
    set file $f

    if { $type=="Setup" } {
	WriteSetup $file
    } elseif { $type=="Schedule" } {
	WriteSchedule $file
    }
}

proc Open { wildcard type } {
    global file setupfile schedulefile
    if ![select $wildcard "Open File..." f "Open $type:"] {
	return
    }
    if ![file exists $f] {
	tkerror "Error:  $f doesn't exist."
	return
    }
    if ![file readable $f] {
	tkerror "Error:  $f isn't readable."
	return
    }
    set file $f
    
    if { $type=="Setup" } {
	ReadSetup $file
        set setupfile $file
    } elseif { $type=="Schedule" } {
	ReadSchedule $file
	set schedulefile $file
    }
}

proc New { wildcard type } {
    global file setupfile schedulefile NumOfEvents \
	NumOfUnits settings schedule
    if ![select $wildcard "New File..." f "New $type:"] {
	return
    }
    if [file exists $f] {
	set response [tk_dialog .overwrite "File Exists" \
	    "File Exists...OverWrite?" question 0 Yes No Cancel]
	
        switch $response {
	    
	    1 { New $wildcard $type }
	    
	    2 { return }
	}
    }
    if [catch "exec touch $f"] {
	tkerror "Error: can't create $f."
	return
    }
    set file $f
    if { $type=="Setup" } {
	set setupfile $file
        unset settings
        set NumOfUnits 0
        DisplayControls
    } elseif { $type=="Schedule" } {
	set schedulefile $file
        unset schedule
        set NumOfEvents 0
    }
}

proc SaveAs { wildcard type } {
    global file
    if ![select $wildcard "Save File As..." f "$type File name:" ] {
	return
    }
    if [file exists $f] {
	set response [tk_dialog .overwrite "File Exists" \
	    "File Exists...OverWrite?" question 0 Yes No Cancel]
	
        switch $response {
	    
	    1 { SaveAs $wildcard $type }
	    
	    2 { return }
	}
    }
    if [catch "exec touch $f"] {
	tkerror "Error: can't create $f."
	return
    }
    set file $f

    if { $type=="Setup" } {
	WriteSetup $file
    } elseif { $type=="Schedule" } {
	WriteSchedule $file
    }
}

proc select {wildcard windowtitle var {message "Open File"} } {

    global selection
    set selection ""
    fileselect $wildcard $windowtitle selectdone $message 
    tkwait window .fileSelectWindow
    uplevel "set $var \"$selection\"" 
    if { $selection == "" } {
	return 0
    } else {
	return 1
    }
}

proc selectdone {f} {
    global selection 
    set selection $f
}

#
# fileselect.tcl --
# simple file selector.
#
# Mario Jorge Silva			          msilva@cs.Berkeley.EDU
# University of California Berkeley                 Ph:    +1(510)642-8248
# Computer Science Division, 571 Evans Hall         Fax:   +1(510)642-5775
# Berkeley CA 94720                                 
# 
# Layout:
#
#  file:                  +----+
#  ____________________   | OK |
#                         +----+
#
#  +------------------+    Cancel
#  | ..               |S
#  | file1            |c
#  | file2            |r
#  |                  |b
#  | filen            |a
#  |                  |r
#  +------------------+
#  currrent-directory
#
# Copyright 1993 Regents of the University of California
# Permission to use, copy, modify, and distribute this
# software and its documentation for any purpose and without
# fee is hereby granted, provided that this copyright
# notice appears in all copies.  The University of California
# makes no representations about the suitability of this
# software for any purpose.  It is provided "as is" without
# express or implied warranty.
#
# modified 10Jan96 by Stew Benedict (benedict@netcom.com)
# to support wildcard calls for particular file types

# names starting with "fileselect" are reserved by this module
# no other names used.

# use the "option" command for further configuration

# this is the default proc  called when "OK" is pressed
# to indicate yours, give it as the first arg to "fileselect"

proc fileselect.default.cmd {f} {
  puts stderr "selected file $f"
}

# this is the default proc called when error is detected
# indicate your own pro as an argument to fileselect

proc fileselect.default.errorHandler {errorMessage} {
    puts stderr "error: $errorMessage"
    catch { cd ~ }
}

# this is the proc that creates the file selector box

proc fileselect {
    {wildcard}
    {windowtitle}
    {cmd fileselect.default.cmd} 
    {purpose "Open file:"} 
    {w .fileSelectWindow} 
    {errorHandler fileselect.default.errorHandler}
     } {

    catch {destroy $w}

    toplevel $w
    grab $w
    wm title $w $windowtitle
    wm geometry $w +200+200

    # path independent names for the widgets
    global fileselect

    set fileselect(entry) $w.file.eframe.entry
    set fileselect(list) $w.file.sframe.list
    set fileselect(scroll) $w.file.sframe.scroll
    set fileselect(ok) $w.bframe.okframe.ok
    set fileselect(cancel) $w.bframe.cancel
    set fileselect(dirlabel) $w.file.dirlabel

    # widgets
    frame $w.file -bd 10 
    frame $w.bframe -bd 10
    pack append $w \
        $w.file {left filly} \
        $w.bframe {left expand frame n}

    frame $w.file.eframe
    frame $w.file.sframe
    label $w.file.dirlabel -anchor e -width 24 -text [pwd] 

    pack append $w.file \
        $w.file.eframe {top frame w} \
	$w.file.sframe {top fillx} \
	$w.file.dirlabel {top frame w}

    label $w.file.eframe.label -anchor w -width 24 -text $purpose
    entry $w.file.eframe.entry -relief sunken 

    pack append $w.file.eframe \
		$w.file.eframe.label {top expand frame w} \
                $w.file.eframe.entry {top fillx frame w} 

    if {$windowtitle=="Save File..."} {
	$fileselect(entry) insert 0 $wildcard
    }

    scrollbar $w.file.sframe.yscroll -relief sunken \
	 -command "$w.file.sframe.list yview"
    listbox $w.file.sframe.list -relief sunken -selectmode single\
	-yscroll "$w.file.sframe.yscroll set" 

    pack append $w.file.sframe \
        $w.file.sframe.yscroll {right filly} \
 	$w.file.sframe.list {left expand fill} 

    # buttons
    frame $w.bframe.okframe -borderwidth 2 -relief sunken
 
    button $w.bframe.okframe.ok -text OK -relief raised -padx 10 \
        -command "fileselect.ok.cmd $wildcard $w $cmd $errorHandler"

    button $w.bframe.cancel -text cancel -relief raised -padx 10 \
        -command "fileselect.cancel.cmd $w"
    pack append $w.bframe.okframe $w.bframe.okframe.ok {padx 10 pady 10}

    pack append $w.bframe $w.bframe.okframe {expand padx 20 pady 20}\
                          $w.bframe.cancel {top}

    # Fill the listbox with a list of the files in the directory (run
    # the "/bin/ls" command to get that information).
    # to not display the "." files, remove the -a option and fileselect
    # will still work
    #
    # changed to glob */ $wildcard to pass mask and get directories
    # 10Jan96  S. Benedict

    $fileselect(list) insert end ".."
    
    foreach i [glob */ $wildcard] {
        if {[string compare $i "."] != 0 && \
	    [string compare $i ".."] != 0 } {
            $fileselect(list) insert end $i
        }
    }

   # Set up bindings for the browser.
    bind $fileselect(entry) <Return> {eval $fileselect(ok) invoke; break}
    bind $fileselect(entry) <Control-c> {eval $fileselect(cancel) invoke; break}

    bind $w <Control-c> {eval $fileselect(cancel) invoke;break}
    bind $w <Return> {eval $fileselect(ok) invoke;break}

    bind $fileselect(list) <Button-1> {
        %W selection set [%W nearest %y]
	$fileselect(entry) delete 0 end
	$fileselect(entry) insert 0 [%W get [%W nearest %y]]
	break
    }

    bind $fileselect(list) <Key> {
        %W selection set [%W nearest %y]
        $fileselect(entry) delete 0 end
	$fileselect(entry) insert 0 [%W get [%W nearest %y]]
	break
    }

    bind $fileselect(list) <Double-ButtonPress-1> {
        %W selection set [%W nearest %y]
	$fileselect(entry) delete 0 end
	$fileselect(entry) insert 0 [%W get [%W nearest %y]]
	$fileselect(ok) invoke
	break
    }

    bind $fileselect(list) <Button-3> {
	%W selection set [%W nearest %y]
        set selected [%W get [%W nearest %y]]
	fileselect_info $selected 
    }

    bind $fileselect(list) <Return> {
        %W selection set [%W nearest %y]
	$fileselect(entry) delete 0 end
	$fileselect(entry) insert 0 [%W get [%W nearest %y]]
	$fileselect(ok) invoke
	break
    }

    # set kbd focus to entry widget

    focus $fileselect(entry)
}

# display file size, date etc.

proc fileselect_info { selected } {
    #set selected [$fileselect(list) get]
    if [file isdirectory $selected] {
        set fileinfo "$selected is a directory!"
    } else {
        if [file isfile $selected] {
	    set fileinfo [exec ls -l $selected]
        } else {
            set filespec "$selected is not a directory or file"
        } 
    tk_dialog .info "File Information" $fileinfo "" 0 OK
    }
}

# auxiliary button procedures

proc fileselect.cancel.cmd {w} {
    destroy $w
}

proc fileselect.ok.cmd {wildcard w cmd errorHandler} {
    global fileselect
    set selected [$fileselect(entry) get]

    # some nasty file names may cause "file isdirectory" to return an error
    set sts [catch { 
	file isdirectory $selected
    }  errorMessage ]

    if { $sts != 0 } then {
	$errorHandler $errorMessage
	destroy $w
	return
    }

    # clean the text entry and prepare the list
    $fileselect(entry) delete 0 end
    $fileselect(list) delete 0 end
    $fileselect(list) insert end ".."

    # selection may be a directory. Expand it.

    if {[file isdirectory $selected] != 0} {
	cd $selected
	set dir [pwd]
	$fileselect(dirlabel) configure -text $dir
 
        foreach i [glob */ $wildcard] {
	    if {[string compare $i "."] != 0 && \
		[string compare $i ".."] != 0} {
		$fileselect(list) insert end $i
	    }
	}
	return
    }

    destroy $w
    $cmd $selected
}

##### end of fileselect code

# main program 
#check for the ~/.x10rc file before we go on

if ![file exists ~/.x10rc] {
    set response [tk_dialog .overwrite "File Exists" \
	"No ~/.x10rc file found!" warning 0 Exit]
	
        switch $response {
	    
	    1 { exit }

	}
}

# set default housecode
set housecode a
set lasthousecode $housecode

# set icon etc.

wm iconbitmap . @x-automate.xbm
wm iconname . x-automate
wm title . x-automate
wm geometry . +50+30

# set prefix for schedules

set prefix { devmap daymap housecode mode minute hour function dimlevel}

#  setup menu structure

frame .menu -relief raised -bd 2
pack .menu -side top -fill x
    
set m .menu.file.m
menubutton .menu.file -text "File" -menu $m -underline 0
menu $m
$m add command -label "Load Default .x10rc" -command \
	{ ReadSetup ~/.x10rc; DisplayControls }
$m add command -label "Open Setup file" -command \
	{ Open *.set Setup; DisplayControls }
$m add command -label "New Setup file" -command {New *.set Setup}
$m add command -label "Save Setup" -command {Save $setupfile Setup}
$m add command -label "Save Setup As ..." \
      -command {SaveAs *.set Setup}
$m add separator
$m add command -label "Open Schedule file" \
      -command { Open *.sch Schedule }
$m add command -label "New Schedule file" \
      -command { New *.sch Schedule }
$m add command -label "Save Schedule" \
      -command { if {$schedulefile!=""} {
	             Save $schedulefile Schedule 
	 	 } else {
		 set response [tk_dialog .message "No Schedule" \
	            "No Schedule Loaded!" warning 0 OK]
	         }
	       }
$m add command -label "Save Schedule As ..." \
      -command { SaveAs *.sch Schedule }
$m add separator
$m add command -label "Display Floorplan" -command {MakeFloorImages $floors}
$m add separator
$m add command -label "Exit x-automate" -command exit    
pack .menu.file -side left

set m .menu.edit.m
menubutton .menu.edit -text "Edit" -menu $m -underline 0
menu $m
$m add command -label "Edit Setup" -command EditSetup
$m add command -label "Edit Schedule" \
      -command { if {$schedulefile!=""} {
	             EditSchedule 
	 	 } else {
		 set response [tk_dialog .message "No Schedule" \
	            "No Schedule Loaded!" warning 0 OK]
	         }
	       }

pack .menu.edit -side left

set m .menu.utility.m
menubutton .menu.utility -text "Utility" -menu $m -underline 0
menu $m
$m add command -label "Query CP-290" -command {Utility "x10-amh -q"}
$m add command -label "Set CP-290 Day/Time" -command { exec x10-amh -s & }
$m add cascade -label "Set Housecode" \
	 -menu .menu.utility.m.housecode -underline 0 
$m add separator
$m add command -label "CP-290 Self Test" -command {Utility "x10-amh -t"}
$m add command -label "List CP-290 Contents" -command ListCP-290
$m add command -label "Upload Schedule To CP-290" -command \
	{ WriteSchedule .x10upload; exec x10-amh .x10upload }

set m .menu.utility.m.housecode
menu $m
set codes { a b c d e f g h i j k l m n o p }
for {set i 0} { $i<[llength $codes] } { incr i} {
  $m add radio -label [lindex $codes $i] -variable housecode \
	-value [lindex $codes $i] -command { SetHousecode $housecode } 
} 

pack .menu.utility -side left

set m .menu.panic.m
menubutton .menu.panic -text "Panic" -menu $m -underline 0
menu $m
$m add command -label "All Lights On" -command \
    { exec x10-amh -n 1,2,3,4,5,6,7,x18,9,10,11,12,13,14,15,16 }
$m add command -label "All Lights Off" -command \
    { exec x10-amh -f 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 }
pack .menu.panic -side left

set m .menu.help.m
menubutton .menu.help -text "Help" -menu $m -underline 0
menu $m
$m add command -label "About x-automate" -command About
$m add separator
$m add command -label "x-automate Help" -command XAutomateHelp

pack .menu.help -side right

# now the pushbuttons

frame .top  -width 96 
frame .bottom -width 96 -height 32

frame .top.left  -width 32 
frame .top.center -width 32 -relief groove -borderwidth 2
frame .top.right  -width 32

set w .

button .exit -text "EXIT" -width 15 -command { exit }

pack .top .bottom -side top -fill x 
pack .top.left .top.center .top.right  -side left  -padx 2 -fill y
pack .exit -in .bottom -side left -expand yes    

# read the .x10rc file with the local setups
 
ReadSetup ~/.x10rc
set setupfile ~/.x10rc
set schedulefile ""

# now display

DisplayControls 
