001/*
002 *  $HeadURL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/isy/ISY99Buttons.java $
003 *  $Revision: 1031 $
004 *  $Date: 2015-11-08 19:13:25 -0800 (Sun, 08 Nov 2015) $
005 *  $Author: tgutwin $
006 */
007
008/*
009 *  IsyButtons -- A simple Panel control pad that turns on/off Insteon
010 *  devices and scenes..
011 *
012 *  Copyright (C) 2010-2013 WebARTS Design, North Vancouver Canada
013 *  http://www.webarts.bc.ca
014 *
015 *  This program is free software; you can redistribute it and/or modify
016 *  it under the terms of Version 2 of the GNU General Public License as
017 *  published by the Free Software Foundation.
018 *
019 *  This program is distributed in the hope that it will be useful,
020 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
021 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
022 *  GNU General Public License for more details.
023 *
024 *  You should have received a copy of the GNU General Public License
025 *  along with this program; if not, write to the Free Software
026 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
027 */
028package ca.bc.webarts.tools.isy;
029
030import ca.bc.webarts.widgets.ColouredLabel;
031import ca.bc.webarts.widgets.JToggleButton;
032import ca.bc.webarts.widgets.Util;
033
034import java.awt.BorderLayout;
035import java.awt.Color;
036import java.awt.Dimension;
037import java.awt.FlowLayout;
038import java.awt.Font;
039import java.awt.GridLayout;
040import java.awt.event.ActionEvent;
041import java.awt.event.ActionListener;
042import java.awt.event.MouseAdapter;
043import java.awt.event.MouseEvent;
044import java.awt.event.MouseListener;
045import java.awt.event.MouseMotionAdapter;
046import java.io.File;
047import java.util.ArrayList;
048import java.util.Enumeration;
049import java.util.Iterator;
050import java.util.NoSuchElementException;
051import java.util.Set;
052import java.util.StringTokenizer;
053import java.util.Vector;
054import javax.swing.BorderFactory;
055import javax.swing.ButtonGroup;
056import javax.swing.ImageIcon;
057import javax.swing.JButton;
058import javax.swing.JCheckBox;
059import javax.swing.JPopupMenu;
060import javax.swing.JRadioButton;
061import javax.swing.JOptionPane;
062import javax.swing.JPanel;
063import javax.swing.JSpinner;
064import javax.swing.JWindow;
065import javax.swing.SpinnerListModel;
066import javax.swing.border.BevelBorder;
067import javax.swing.border.EmptyBorder;
068
069import com.udi.insteon.client.InsteonConstants;
070import com.universaldevices.client.NoDeviceException;
071import com.universaldevices.common.UDUtil;
072import com.universaldevices.device.model.ProductInfo;
073import com.universaldevices.device.model.UDGroup;
074import com.universaldevices.device.model.UDControl;
075import com.universaldevices.device.model.UDNode;
076import com.universaldevices.resources.errormessages.Errors;
077
078/**
079 *  A simple window widget that holds a bunch of buttons that directly turn an
080 *  Insteon device on or off via a Universal Devices ISy-99i. This app requires
081 *  a few other libraries: webarts, and
082 *  kiwi java libraries as well as the UDI JSDK
083 *  implementation on your system.
084 *
085 * @author    tgutwin
086 */
087public class ISY99Buttons extends JWindow implements ActionListener
088{
089  /**  A Class holder for its name (used in Logging).  */
090  private static String className_ = "ISY99Buttons";
091
092  /**  A holder for this clients System File Separator.  */
093  public final static String SYSTEM_FILE_SEPERATOR = File.separator;
094
095  /**  A holder for this clients System line termination separator.  */
096  public final static String SYSTEM_LINE_SEPERATOR =
097                                           System.getProperty("line.separator");
098  private IsyInsteonClient myISY = null;
099  private boolean stillRunning_ = false;
100  private boolean finishedStartup_ = false;
101
102  private String[] insteonAddresses_ = {"14.2B.31.1"};
103  private String[] insteonResponderAddresses_ = {"14.2B.31.1"};
104  private String[] insteonResponderNames_ = {"Device1"};
105
106  /** The last device button pressed - used for dim/bright. */
107  private String lastDevice_="";
108
109  // Colours
110  /**  The colour used for all panels except the display area.  */
111  static Color mainBackColour_ = new Color( 100, 180, 245 );
112  /**  The colour used to back the display area.  */
113  static Color displayBackColour_ = Color.black;
114  /**  The display area text colour.  */
115  static Color displayTextColour_ = mainBackColour_.brighter();
116
117  /**  The Main Popup menu  */
118  static JPopupMenu mainMenu = new JPopupMenu( className_ + " Main Menu" );
119
120  /**  Popup menu Mouse Listener.  */
121  static MouseListener popupListener_ =
122          new MouseAdapter()
123          {
124            public void mousePressed( MouseEvent e )
125            {
126              maybeShowPopup( e );
127            }
128
129            public void mouseReleased( MouseEvent e )
130            {
131              maybeShowPopup( e );
132            }
133
134            private void maybeShowPopup( MouseEvent e )
135            {
136              if ( e.isPopupTrigger() )
137              {
138                mainMenu.show( e.getComponent(), e.getX(), e.getY() );
139              }
140            }
141          };
142
143
144  /**  The wrapper panel holding all in this JWindow.  */
145  JPanel dragableMiniViewPanel = new JPanel();
146  /**  The Button area wrapper inside the dragableMiniViewPanel.  */
147  JPanel miniViewPanel = new JPanel();
148  /**  The Control area at the top of the window.  */
149  JPanel miniViewWindowControlPanel = new JPanel();
150  /**  The window header / draggable button.  */
151  JButton miniDragButton = new JButton( "" );
152  /**  The button to Min/Max this mini app window  */
153  JButton miniButtonView_ = new JButton( "" );
154  /**  The button to close this mini app window.  */
155  JButton miniButtonClose_ = new JButton( "" );
156  /**  The panel holding the X10 buttons  */
157  JPanel miniButtonPanel_ = new JPanel();
158  /**  The panel holding the on/off toggle button  */
159  JPanel dimPanel_ = new JPanel();
160  /**  The object holding the min/max icon.  */
161  ImageIcon iconMiniView_ = new ImageIcon();
162  /**  The object holding the close icon.  */
163  ImageIcon iconMiniClose_ = new ImageIcon();
164  /**  The object holding the dim icon.  */
165  ImageIcon iconDim_ = new ImageIcon();
166  /**  The object holding the bright icon.  */
167  ImageIcon iconBright_ = new ImageIcon();
168  /**  The X amount the current window was just dragged.  */
169  private int XDifference_;
170  /**  The Y amount the current window was just dragged. * */
171  private int YDifference_;
172  /**  the x position of the mini window. * */
173  private int xPosition_ = 100;
174  /**  the y position of the mini window. * */
175  private int yPosition_ = 100;
176  /**  the x position of the app window. * */
177  private int appXpos = -1;
178  /**  the y position of the app window. * */
179  private int appYpos = -1;
180  /**  the number of rows of buttons to display in the app window. * */
181  private int displayRows_ = 0;  // zero means any amount needed
182  /**  the number of columns of buttons to display in the app window. * */
183  private int displayCols_ = 3;
184  /**  Class var holding the number of  buttons * */
185  private int numButtons_ = insteonResponderAddresses_.length;
186
187  /**  The dir where the images are located.  */
188  String IMAGE_DIR = "." + SYSTEM_FILE_SEPERATOR +
189                     "images" + SYSTEM_FILE_SEPERATOR;
190
191  /**  The Extension for the button images * */
192  String buttonImageExtension_ = ".gif";
193
194  /**  The Extension for the button images * */
195  String buttonImagepath_ = "/images/org/javalobby/icons/20x20/Circle_";
196
197  /**  This is this apps draggable - Title bar.  */
198  ColouredLabel miniDragLabel = new ColouredLabel(
199      displayBackColour_,
200      displayTextColour_,
201      new Font( "Arial", Font.PLAIN, 10 ),
202      " <--     ISY994i Buttons     --> " );
203
204  /**  An array of the  buttons.  */
205  JButton[] controlButton_ = null;
206
207  /**  A dim button.  */
208  JButton dimButton_ = new JButton( "" );
209
210  /**  A dim button.  */
211  JButton brightButton_ = new JButton( "" );
212
213  /**  The toggle controlling if the next command will be on or Off.  */
214  JToggleButton onOffToggle_ = new JToggleButton();
215
216  ImageIcon onIcon =  new ImageIcon( Util.loadImage( "/images/org/javalobby/icons/20x20/BulbOn.gif"  ), "On " + className_ );
217  ImageIcon offIcon =  new ImageIcon( Util.loadImage( "/images/org/javalobby/icons/20x20/BulbOff.gif" ), "Off " + className_ );
218
219  /**
220   *  Listener for when the mouse is pressed on the fake window frame title bar
221   *  button.
222   */
223  MouseAdapter miniViewMouseListener =
224    new MouseAdapter()
225    {
226      /**
227       *  the impl for a mouse press.
228       *
229       * @param  e  Description of Parameter
230       * @since
231       */
232      public void mousePressed( MouseEvent e )
233      {
234        XDifference_ = e.getX();
235        YDifference_ = e.getY();
236      }
237    };
238
239  /**  Listener for changing the location of this window. *  */
240  MouseMotionAdapter dragMiniViewMotionListener =
241    new MouseMotionAdapter()
242    {
243      /**
244       *  Implementation of the mouse dragged event handler.
245       *
246       * @param  e  Description of Parameter
247       * @since
248       */
249      public void mouseDragged( MouseEvent e )
250      {
251        setLocation( xPosition_ + ( e.getX() - XDifference_ ),
252            yPosition_ + ( e.getY() - YDifference_ ) );
253        xPosition_ = xPosition_ + ( e.getX() - XDifference_ );
254        yPosition_ = yPosition_ + ( e.getY() - YDifference_ );
255      }
256    };
257
258
259  /** The thread that the isyDevice runs in. **/
260  private Thread startupThread_ = new Thread ()
261      {
262        public void run()
263        {
264          myISY.start();
265        }
266      };
267
268
269  /** The thread that watches for changes to the nodes being displayed.  It then keeps the icon indicators in sync with actual. **/
270  private Thread nodeWatcherThread_ = new Thread ()
271      {
272        public void run()
273        {
274          System.out.println("\n *!*! Watching for nodeChanges...");
275          UDUtil.sleep(750);
276          ArrayList<NodeChangeMessage> msgs = null;
277          int i = 0;
278          boolean notFound = true;
279          //NodeChangeMessage currMsg = null;
280          //timeOut+=1000;
281          while (startupThread_!=null)
282          {
283            // Get the latest Changes
284            msgs = myISY.getRecentNodeChanges();
285
286
287            // Update the icons
288            if (msgs!=null && finishedStartup_)  // ignore the 1st time through
289            {
290              for (NodeChangeMessage currMsg:msgs)
291              {
292                if (currMsg!=null)
293                {
294                  //System.out.println(" *!*! "+msgs.size()+ " nodeChanges...");
295                  //currMsg = (NodeChangeMessage)msgs.get(0);
296                  UDControl msgControl =  currMsg.getControl();
297                  Object msgValue = currMsg.getValue();
298                  UDNode msgNode = currMsg.getNode();
299
300                  /*
301                  System.out.println("       "+
302                                      (msgNode!=null?msgNode.name:"nullNode") + " changed "+
303                                      (msgControl!=null?msgControl.label:"nullControl") + " to "+
304                                      (msgValue!=null?""+msgValue:"nullValue")
305                                    );
306                  */
307                  notFound = true;
308                  for ( i = 0; notFound && i < numButtons_; i++ )
309                  {
310                    // NON-X10 buttons get updated
311                    if (!insteonResponderNames_[i].startsWith("A"))
312                    {
313                      if(controlButton_[i].getText().equals((msgNode!=null?msgNode.name:"nullNode")))
314                      {
315                        notFound = false; // break out early
316                        // update Icon
317                        if ((msgControl!=null?msgControl.label:"nullControl").equals("Status") &&
318                             !(msgValue!=null?""+msgValue:"nullValue").equals("0") )
319                          controlButton_[i].setIcon(onIcon);
320                        if ((msgControl!=null?msgControl.label:"nullControl").equals("Status") &&
321                             (msgValue!=null?""+msgValue:"nullValue").equals("0") )
322                          controlButton_[i].setIcon(offIcon);
323                      }
324                    }
325                  }
326                }
327              }
328              msgs = null;
329            }
330            else if (msgs!=null && !finishedStartup_) finishedStartup_ = true;
331
332            // wait a bit then do it again
333            UDUtil.sleep(200);
334          }
335        }
336      };
337
338
339    /** Indicates if this class thread is still running **/
340    public boolean stillRunning( )
341    {
342      return this.stillRunning_;
343    }
344
345
346    /** Gets the issyClient running and waits till its running. **/
347    public boolean startIsy( )
348    {
349      System.out.print(" **Starting Client...");
350      startupThread_.start();
351      UDUtil.sleep(1500);
352      int timeOut = 0;
353      while (!myISY.isReadyToGo()  && timeOut < 25000)
354      {
355        UDUtil.sleep(250);
356        timeOut+=250;
357      }
358      if (timeOut>24999)
359        System.out.println(" Startup Timed Out.");
360      else
361        System.out.println(" Startup Successful.");
362      this.stillRunning_ = myISY.isReadyToGo();
363      if (!this.stillRunning_)
364      {
365        UDUtil.sleep(2500);
366        this.stillRunning_ = myISY.isReadyToGo();
367      }
368      UDUtil.sleep(2500);
369      return this.stillRunning_ ;
370    }
371
372
373    /**
374     * Cleans   up any ISY cleanup procedures in prep for exit.
375     */
376  protected  void isyCleanup()
377  {
378    System.out.print("\nCleaning up before Exit... ");
379    try
380    {
381      nodeWatcherThread_.stop();
382      startupThread_ = null;
383      this.stillRunning_ = false;
384      UDUtil.sleep(250);
385      myISY.cleanUp();
386      myISY.stop();
387      //startupThread_.stop();
388      //Util.sleep(5000);
389      //if (myISY.isReadyToGo()) { System.out.println(" waiting...");UDUtil.sleep(2000);}
390
391      Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
392      Thread currThread = null;
393      Iterator iter = threadSet.iterator();
394      while (iter.hasNext())
395      {
396        currThread = (Thread) iter.next();
397        System.out.print(currThread.getName());
398        try
399        {
400          if (currThread.getName().startsWith("Thread"))
401          {
402                      System.out.print("[X]");
403                      currThread.interrupt();
404          }
405          System.out.print(" ");
406        }
407         catch (Exception ex)
408        {
409          // nothing
410        }
411      }
412    }
413    catch (Exception ex)
414    {
415      // nothing
416    }
417    System.out.println("done!");
418
419  }
420
421
422    /**
423     * Notifies the user of a syntax error
424     */
425  protected void syntaxError()
426  {
427    System.err.println("Syntax error. Try again.");
428  }
429
430
431  /**  Constructor BUT does NOT start up the Client Connection. To start use startIsy.**/
432  public ISY99Buttons()
433  {
434    myISY = new IsyInsteonClient(true);
435    myISY.debugOn();
436  }
437
438
439  /**
440   *  The main program for the ISY99Buttons class.
441   *
442   * @param  arg  The command line arguments
443   */
444  public static void main( String[] arg )
445  {
446    System.out.println( "Starting ISY99Buttons " );
447    ISY99Buttons instance = new ISY99Buttons();
448    Errors.addErrorListener(new MyISYErrorHandler());
449
450    try
451    {
452      instance.startIsy(); // this starts and BLOCKS until started
453      if (instance.stillRunning_  && instance.myISY.isReadyToGo())
454      {
455        instance.myISY.debugOff();
456        Thread.sleep(200);
457        instance.loadButtonNodeAddresses();
458        instance.loadButtonNodeNames();
459        instance.initGui();
460        System.out.println(ca.bc.webarts.tools.isy.ISY99CLI.dumpIsyProductInfo(instance.myISY).toString());
461        System.out.println("\n\n");
462      }
463      else
464      {
465        // put up a dialog to say no device authentication
466        JOptionPane.showMessageDialog(null, "Could Not Find/Authenticate any UDI ISY-99 devices.\nTry Again.",
467                                                               "Alert", JOptionPane.ERROR_MESSAGE);
468        Thread.sleep(3000);
469        if (instance.stillRunning_  && instance.myISY.isReadyToGo())
470        {
471          instance.myISY.debugOff();
472          Thread.sleep(200);
473          instance.loadButtonNodeAddresses();
474          instance.loadButtonNodeNames();
475          instance.initGui();
476          System.out.println(ca.bc.webarts.tools.isy.ISY99CLI.dumpIsyProductInfo(instance.myISY).toString());
477          System.out.println("\n\n");
478        }
479        else
480        {
481          // put up a dialog to say no device authentication
482          JOptionPane.showMessageDialog(null, "Still No Go.\nCould Not Find/Authenticate any UDI ISY-99 devices.\nGiving up.",
483                                                             "Alert", JOptionPane.ERROR_MESSAGE);
484        }
485      }
486
487      // hang tight until the window closes
488      while (instance.stillRunning_ )
489      {
490        UDUtil.sleep(500);
491      }
492    }
493    catch (Exception e)
494    {
495      e.printStackTrace();
496    }
497    finally
498    {
499      instance.isyCleanup();
500    }
501    System.exit(0);
502  }
503
504
505  /** Gets The Window Up **/
506  private void initGui()
507  {
508
509    initWindowPanels();
510    pack();
511    setLocation( xPosition_, yPosition_ );
512    setVisible( true );
513    toFront();
514
515    nodeWatcherThread_.start();
516
517  }
518
519
520  /**
521   *  Handles all the Actions originating from the Control Buttons.
522   *
523   * @param  e  The action event to handle
524   */
525  public void actionPerformed( ActionEvent e )
526  {
527    final String methodName = className_ + ": actionPerformed()";
528
529    JButton pressedButton = (JButton)( e.getSource() );
530    ImageIcon buttonIcon = (ImageIcon)pressedButton.getIcon();
531    String command = "";
532    int commandButtonNumber = 0;
533
534    if ( buttonIcon != null && !buttonIcon.getDescription().startsWith("On") && !buttonIcon.getDescription().startsWith("Off"))
535    {
536      command = buttonIcon.getDescription();
537      //System.out.println("Action From: "+command);
538      if ( command.trim().equals( "Close " + className_ ) )
539      {
540        isyCleanup();
541        //System.exit( 0 );
542      }
543
544      boolean dim = command.trim().startsWith("dim");
545      boolean bright = command.trim().startsWith("bright");
546      if (!lastDevice_.equals("") && ( dim || bright) )
547      {
548        System.out.println(lastDevice_+"  to "+(dim?"Dim":"Brighten") );
549        System.out.println(" Device Status: "+getStatus(getNodeFromAddress(lastDevice_.replace(" ","."))));
550        if (dim)
551          {
552            //myISY.dimDevice(lastDevice_);
553            myISY.dimDevice(lastDevice_.replace("."," "));
554          }
555        else if (bright)
556          {
557            //myISY.brightenDevice(lastDevice_);
558            myISY.brightenDevice(lastDevice_.replace("."," "));
559          }
560      }
561    }
562    else if ( buttonIcon != null && (buttonIcon.getDescription().startsWith("On") || buttonIcon.getDescription().startsWith("Off")))
563    {
564      String deviceAddress = pressedButton.getToolTipText();
565      String onOffCommand = ( onOffToggle_.getState() ? InsteonConstants.DEVICE_ON : InsteonConstants.DEVICE_OFF );
566
567      System.out.print("Button Pressed: "+onOffCommand+" "+deviceAddress );
568      {
569        UDNode n = getNodeFromAddress(deviceAddress.replace(" ","."));
570        String deviceStatus = getStatus(n);
571        // System.out.println("  to turn  "+(onOffToggle_.getState()?"On":"Off") );
572        System.out.println("  to turn  "+(deviceStatus.equals("0")?"On":"Off") );
573        System.out.println(" Device Status: "+deviceStatus);
574        //processCommand( onOffCommand+" "+Util.tokenReplace(deviceAddress," ",".") );
575
576        //if (onOffToggle_.getState())
577        if (deviceStatus==null||deviceStatus.equals("0"))
578        {
579          //myISY.turnDeviceOn(deviceAddress);
580          myISY.turnDeviceOn(deviceAddress.replace("."," "));
581          // Update the ImageIcon
582          pressedButton.setIcon(onIcon);
583        }
584        else
585        {
586          //myISY.turnDeviceOff(deviceAddress);
587          myISY.turnDeviceOff(deviceAddress.replace("."," "));
588        // Update the ImageIcon
589          pressedButton.setIcon(offIcon);
590        }
591        lastDevice_ = deviceAddress;
592
593        // Update the ImageIcon
594        //deviceStatus = getStatus(n);
595        //if (deviceStatus!=null && !deviceStatus.equals("")) pressedButton.setIcon(deviceStatus.equals("0")?offIcon:onIcon);
596      }
597    }
598  }
599
600
601    /**
602     * Returns all <code>UDNode</code>s that are associated in the client.
603     * @return the  Enumeration UDNode if found, null otherwise
604     */
605  protected Enumeration <UDNode> getNodes()// throws com.universaldevices.client.NoDeviceException
606  {
607    Enumeration <UDNode> isyNodes = null;
608    try
609    {
610      isyNodes = myISY.getNodes().elements();
611    }
612    catch (Exception ex)  // probably com.universaldevices.client.NoDeviceException
613    {
614      // send empty
615    }
616    return isyNodes;
617
618    //return myISY.getNodes().elements();
619  }
620
621
622    /**
623     * Returns all<code>UDNode</code>s that are associated in the client.
624     * @return the  Enumeration <UDNode> if found, null otherwise
625     */
626  protected Enumeration <UDNode> getMyNodes()// throws com.universaldevices.client.NoDeviceException
627  {
628    Enumeration <UDNode> isyNodes = null;
629    Vector <UDNode> myNodes =new Vector<UDNode>();
630
631    try
632    {
633      isyNodes = myISY.getNodes().elements();
634      UDNode node = null;
635
636      while ( isyNodes.hasMoreElements())
637      {
638        node = (UDNode) isyNodes.nextElement();
639        // System.out.println(" Check To Add "+node.address + ":" + node.name+" type="+node.typeReadable+"  -->"+ !node.typeReadable.startsWith("00."));
640        myNodes.add(node);
641      }
642    }
643    catch (Exception ex)  // probably com.universaldevices.client.NoDeviceException
644    {
645      // send empty
646    }
647    return myNodes.elements();
648  }
649
650
651    /**
652     * Returns all NON-ControlLinc or Non-SwitchLinc <code>UDNode</code>s that are associated in the client.
653     * @return the  Enumeration <UDNode> if found, null otherwise
654     */
655  protected Enumeration <UDNode> getMyControlableNodes()// throws com.universaldevices.client.NoDeviceException
656  {
657    Enumeration <UDNode> isyNodes = null;
658    Vector <UDNode> myNodes =new Vector<UDNode>();
659
660    try
661    {
662      isyNodes = myISY.getNodes().elements();
663      UDNode node = null;
664
665      while ( isyNodes.hasMoreElements())
666      {
667        node = (UDNode) isyNodes.nextElement();
668        //System.out.println(" Check To Add "+node.address + ":" + node.name+" type="+node.typeReadable+"  -->"+ !node.typeReadable.startsWith("00."));
669        if (!node.typeReadable.startsWith("00.00") && /* ContolLinc */
670            !node.typeReadable.startsWith("00.05") && /* RemoteLinc */
671            !node.typeReadable.startsWith("00.10") && /* RemoteLinc2 */
672            !node.typeReadable.startsWith("01.09") && /* SwitchLinc */
673            !node.typeReadable.startsWith("05.03") && /* thermostat */
674            !node.typeReadable.startsWith("01.1B") && /* switch */
675            !node.typeReadable.startsWith("03.15") && /* USB connection */
676            !node.typeReadable.startsWith("10.01") && /* motion sensor */
677            !node.typeReadable.startsWith("09.07")  /* iMeter Solo */
678           )
679          myNodes.add(node);
680
681        if (node.typeReadable.startsWith("01.1B") && /* SwitchLinc */
682            node.address.endsWith("1") )  /* only add the MAIN switch */
683          myNodes.add(node);
684
685
686      }
687    }
688    catch (Exception ex)  // probably com.universaldevices.client.NoDeviceException
689    {
690      // send empty
691    }
692    return myNodes.elements();
693  }
694
695
696  /**
697   * Forks a thread and watches myIsy for updates to the nodes being watched.
698   **/
699  private void watchMyNodes()
700  {
701  }
702
703
704  /**
705   * Counts all the NON-ContolLinc Nodes that the ISY is aware of.
706   * @return the number of  NON-ContolLinc nodes  that the ISY is aware of.
707   **/
708  private int countMyNodes()
709  {
710    int retVal = 0;
711    try
712    {
713      Enumeration <UDNode> e = getMyNodes();
714      UDNode node = null;
715      while ( e.hasMoreElements())
716      {
717        node = e.nextElement();
718        retVal++;
719      }
720    }
721    catch (Exception ex)
722    {
723      ex.printStackTrace();
724    }
725    return retVal;
726  }
727
728
729  /**
730   * Counts all the NON-ContolLinc Responder Nodes that the ISY is aware of.
731   * @return the number of  NON-ContolLinc Responder nodes that the ISY is aware of.
732   **/
733  private int countMyControlableNodes()
734  {
735    int retVal = 0;
736    try
737    {
738      Enumeration <UDNode> e = getMyControlableNodes();
739      UDNode node = null;
740      while ( e.hasMoreElements())
741      {
742        node = e.nextElement();
743        //if (node.isResponder())
744          retVal++;
745      }
746    }
747    catch (Exception ex)
748    {
749      ex.printStackTrace();
750    }
751    return retVal;
752  }
753
754
755  /**
756   * Puts all the Node Address at this ISY into the insteonAddresses_ array.
757   **/
758  private void loadNodeAddresses()
759  {
760    try
761    {
762      int numNodes = countMyNodes();
763      insteonAddresses_ = new String[numNodes];
764      System.out.println("\nLoading "+numNodes+" Button Devices");
765      int count = 0;
766      Enumeration <UDNode> e = getMyNodes();
767      while (e.hasMoreElements())
768      {
769        UDNode node = e.nextElement();
770        insteonAddresses_[count++] = Util.tokenReplace(node.address," ",".");
771        System.out.println("                 "+node.address + ":" + node.name+" type="+node.typeReadable);
772      }
773      numButtons_ = numNodes;
774      //controlButton_ = new JButton[numNodes];
775    }
776    catch (Exception ex)
777    {
778      ex.printStackTrace();
779    }
780
781  }
782
783
784  /**
785   * Puts all the Node Address that are responders at this ISY into the insteonResponderAddresses_ array so they can get assigned to buttons.
786   **/
787  private void loadButtonNodeAddresses()
788  {
789    try
790    {
791      int numNodes = countMyControlableNodes();
792      insteonResponderAddresses_ = new String[numNodes];
793      System.out.println("\nLoading "+numNodes+" Button Devices");
794      int count = 0;
795      Enumeration <UDNode> e = getMyControlableNodes();
796      while (e.hasMoreElements())
797      {
798        UDNode node = e.nextElement();
799        insteonResponderAddresses_[count++] = Util.tokenReplace(node.address," ",".");
800        System.out.println("                 "+node.address + ":" + node.name+" type="+node.typeReadable);
801      }
802      numButtons_ = numNodes;
803      if (controlButton_==null)
804        controlButton_ = new JButton[numNodes];
805    }
806    catch (Exception ex)
807    {
808      ex.printStackTrace();
809    }
810
811  }
812
813
814  /**
815   * Puts all the Node Names that are responders at this ISY into the insteonResponderNamees_ array so they can get assigned to buttons.
816   **/
817  private void loadButtonNodeNames()
818  {
819    try
820    {
821      int numNodes = countMyControlableNodes();
822      insteonResponderNames_ = new String[numNodes];
823      System.out.println("\nLoading "+numNodes+" Button Devices");
824      int count = 0;
825      Enumeration <UDNode> e = getMyControlableNodes();
826      while (e.hasMoreElements())
827      {
828        UDNode node = e.nextElement();
829        insteonResponderNames_[count++] = node.name;
830        //System.out.println("                 "+node.address + ":" + node.name+" type="+node.typeReadable);
831      }
832      numButtons_ = numNodes;
833      if (controlButton_==null)
834        controlButton_ = new JButton[numNodes];
835    }
836    catch (Exception ex)
837    {
838      ex.printStackTrace();
839    }
840
841  }
842
843
844    /**
845     * Returns the current value of Insteon Devicse (its state)
846     * @param tk - the StringTokenzier
847     */
848  protected void processStatus(StringTokenizer tk)
849  {
850    String tmp = tk.nextToken();        //device address
851    UDNode node = getNodeFromAddress(tmp);
852    if (node == null)
853    {
854      return;
855    }
856
857    tmp = (String) myISY.getCurrValue(node, InsteonConstants.DEVICE_STATUS);
858    System.out.println("The current status for " + node.address + "/" + node.name + " is " + tmp);
859  }
860
861
862    /**
863     * Returns the current value of an Insteon Device (its state)
864     * @param node - the UDNode to query
865     * @return the status
866     **/
867  protected String getStatus(UDNode node )
868  {
869    if (node == null)
870    {
871      return "";
872    }
873
874    String tmp = (String) myISY.getCurrValue(node, InsteonConstants.DEVICE_STATUS);
875    //stem.out.println("The current status for " + node.address + "/" + node.name + " is " + tmp);
876
877    return tmp;
878  }
879
880  /** Duh!
881   * @param the node's address to query.
882   * @return the name assigned to the node at the address requested.
883   **/
884  protected String getNodeNameFromAddress(String address)
885  {
886    if (address == null)
887    {
888      System.err.println("Missing Device/Scene address");
889      return "";
890    }
891    UDNode node = getNodeFromAddress(address);
892    return node.name;
893  }
894
895
896    /**
897     * Returns a <code>UDGroup</code> or a <code>UDNode</code> based on the
898     * given address
899     * @param address - the address of the node/scene to be retrieved
900     * @return the UDNode if found, null otherwise
901     */
902  protected UDNode getNodeFromAddress(String address)
903  {
904    if (address == null)
905    {
906      System.err.println("Missing Device/Scene address");
907      return null;
908    }
909    if (address.indexOf(".") > 0)
910    {
911      //this is an insteon device
912      address = address.replace(".", " ");            //normalize it to our format
913      try
914      {
915        UDNode node = myISY.getNodes().get(address);
916        if (node == null)
917        {
918          System.err.println(" Address points to a non existing Insteon Device");
919          return null;
920        }
921        return node;
922      }
923      catch (NoDeviceException e)
924      {
925        System.out.println(e);
926        return null;
927      }
928    }
929
930    //this is an insteon scene
931    try
932    {
933      UDGroup group = myISY.getGroups().get(address);
934      if (group == null)
935      {
936        System.err.println("Address points to a non-existing scene");
937        return null;
938      }
939      return group;
940    }
941    catch (Exception e)
942    {
943      System.err.println(e);
944      return null;
945    }
946  }
947
948    /**
949     * Returns a <code>UDGroup</code> or a <code>UDNode</code> based on the
950     * given name
951     * @param address - the address of the node/scene to be retrieved
952     * @return the UDNode if found, null otherwise
953     */
954  protected UDNode getNode(String nodeName)
955  {
956    if (nodeName == null)
957    {
958      System.err.println("Missing Device/Scene name");
959      return null;
960    }
961    try
962    {
963      Enumeration <UDNode> isyNodes = null;
964
965      isyNodes = getNodes();
966      UDNode node = null;
967      boolean notFound = true;
968
969      while (notFound && isyNodes.hasMoreElements())
970      {
971        node = (UDNode) isyNodes.nextElement();
972        if (nodeName.equals(node.name)) notFound = false;
973      }
974
975      if (node == null)
976      {
977        System.err.println("Address points to a non existing Insteon Device");
978        return null;
979      }
980      return node;
981    }
982    catch (Exception e)
983    {
984      System.out.println(e);
985      return null;
986    }
987  }
988
989  protected char INSTEON_MASTER_MODE = 0xF0;
990  protected char INSTEON_SLAVE_MODE = 0x0F;
991  /**
992   * Returns the mode based on the input
993   * @param mode
994   * @return - the mode (INSTEON_MASTER_MODE, INSTEON_SLAVE_MODE)
995   **/
996  protected char getMode(String mode)
997  {
998    if (mode.equals("M"))
999    {
1000      return INSTEON_MASTER_MODE;
1001    }
1002
1003    if (mode.equals("S"))
1004    {
1005      return INSTEON_SLAVE_MODE;
1006    }
1007
1008    System.err.println("Please specify M (for Master mode) or S (for Slave mode)");
1009    return 0;
1010  }
1011
1012    /**
1013     * Processes an Insteon command
1014     * @param cmd - the command to be processed
1015     * @param tk - the StringTokenizer
1016     */
1017  protected void processInsteonCommand(String cmd, StringTokenizer tk)
1018  {
1019    String address = tk.nextToken();
1020    System.out.println("tk addr:"+address);
1021    UDNode node = getNodeFromAddress(address.replace(" ","."));
1022    if (node == null)
1023    {
1024      return;
1025    }
1026
1027        //it's a group
1028    if (node instanceof UDGroup)
1029    {
1030      myISY.changeGroupState(cmd, null, node.address);
1031    }
1032    else
1033    {
1034      myISY.changeNodeState(cmd, null, node.address);
1035    }
1036  }
1037
1038
1039  /**
1040   *  Abstracts the actual sending of the ISY Command.
1041   *
1042   * @param  command       This apps Command (not necessarily an Insteon Command (but could Be)
1043   */
1044  public void processCommand( String command)
1045  {
1046    // Now disable all buttons and send the X10 command
1047    enableDeviceButtons( false );
1048
1049    if (command == null || command.length() < 1)
1050    {
1051      return;
1052    }
1053
1054    System.out.println("Button Command:"+command);
1055    StringTokenizer tk = new StringTokenizer(command, " ");
1056    try
1057    {
1058      String cmd = tk.nextToken();
1059      if (  cmd.startsWith(InsteonConstants.DEVICE_ON) ||
1060            cmd.startsWith(InsteonConstants.DEVICE_OFF) ||
1061            cmd.startsWith(InsteonConstants.DEVICE_FAST_ON) ||
1062            cmd.startsWith(InsteonConstants.DEVICE_FAST_OFF) ||
1063            cmd.startsWith(InsteonConstants.LIGHT_DIM) ||
1064            cmd.startsWith(InsteonConstants.LIGHT_BRIGHT))
1065      {
1066        System.out.println("Processing Cmd:"+cmd+" tk:"+tk.toString());
1067        processInsteonCommand(cmd, tk);
1068      }
1069      else if (cmd.equalsIgnoreCase("StartLinking"))
1070      {
1071         myISY.startLinking();
1072      }
1073      else if (cmd.equalsIgnoreCase("StopLinking"))
1074      {
1075         myISY.stopLinking();
1076      }
1077      else if (cmd.startsWith("SetLinkingMode"))
1078      {
1079        //setLinkingMode(tk);
1080      }
1081      else if (cmd.startsWith("Rename"))
1082      {
1083        //processRename(tk);
1084      }
1085      else if (cmd.startsWith("Delete"))
1086      {
1087       // processDelete(tk);
1088      }
1089      else if (cmd.startsWith("Remove"))
1090      {
1091        //processRemoveFromScene(tk);
1092      }
1093      else if (cmd.startsWith("Move"))
1094      {
1095       // processMove(tk);
1096      }
1097      else if (cmd.startsWith("NewScene"))
1098      {
1099       // processNewScene(tk);
1100      }
1101      else if (cmd.startsWith("SetSceneOnLevel"))
1102      {
1103        //processSceneOnLevel(tk);
1104      }
1105      else if (cmd.startsWith("SetSceneRampRate"))
1106      {
1107       // processSceneRampRate(tk);
1108      }
1109      else if (cmd.startsWith("SetSceneControllerOnLevel"))
1110      {
1111       // processSceneControllerOnLevel(tk);
1112      }
1113      else if (cmd.startsWith("SetSceneControllerRampRate"))
1114      {
1115        //processSceneControllerRampRate(tk);
1116      }
1117      else if (cmd.equalsIgnoreCase("ListNodes"))
1118      {
1119       // processListNodes();
1120      }
1121      else if (cmd.equalsIgnoreCase("ListScenes"))
1122      {
1123       // processListScenes();
1124      }
1125      else if (cmd.startsWith("GetStatus"))
1126      {
1127        //processStatus(tk);
1128      }
1129      else if (cmd.equalsIgnoreCase("Exit"))
1130      {
1131        isyCleanup();
1132        //System.exit(0);
1133      }
1134      else
1135      {
1136        syntaxError();
1137      }
1138    } // try
1139    catch (Exception e)
1140    {
1141      e.printStackTrace();
1142      syntaxError();
1143    }
1144    //Util.sleep(2500);
1145    enableDeviceButtons( true );
1146  }
1147
1148
1149  /**
1150   *  Creates a text button using the node name as the button text, and the node's address as the toolTipText.
1151   *
1152   * @param  controlAddress is the address of the Insteon Device to set this button up for.
1153   * @return                A Button
1154   */
1155  private JButton createButtonFromName( String controlName )
1156  {
1157    ImageIcon onIcon =  new ImageIcon( Util.loadImage( "/images/org/javalobby/icons/20x20/BulbOn.gif"  ), "On " + className_ );
1158    ImageIcon offIcon =  new ImageIcon( Util.loadImage( "/images/org/javalobby/icons/20x20/BulbOff.gif" ), "Off " + className_ );
1159    String nodeAddress = getNode( controlName).address ;
1160    //String nodeName_trunc =
1161    JButton retVal = new JButton( controlName );
1162    // TO_DO: Add a custom tooltip naming the device on the end of this num
1163    retVal.setToolTipText( nodeAddress );
1164    String deviceStatus = getStatus(getNode(controlName));
1165    if (deviceStatus!=null && !deviceStatus.equals("")) retVal.setIcon(deviceStatus.equals("0")?offIcon:onIcon);
1166    retVal.addActionListener( this );
1167    retVal.setBorder( BorderFactory.createBevelBorder(
1168        BevelBorder.RAISED ) );
1169    retVal.setFont( retVal.getFont().deriveFont(10.0f));
1170    return retVal;
1171  }
1172
1173
1174  /**
1175   *  Creates a text button using the node name as the button text, and the node's address as the toolTipText.
1176   *
1177   * @param  controlAddress is the address of the Insteon Device to set this button up for.
1178   * @return                A Button
1179   */
1180  private JButton createButtonFromAddress( String controlAddress )
1181  {
1182    String nodeName = getNodeNameFromAddress( controlAddress) ;
1183    //String nodeName_trunc =
1184    JButton retVal = new JButton( nodeName );
1185    // TO_DO: Add a custom tooltip naming the device on the end of this num
1186    retVal.setToolTipText( controlAddress );
1187    retVal.addActionListener( this );
1188    retVal.setBorder( BorderFactory.createBevelBorder(
1189        BevelBorder.RAISED ) );
1190    retVal.setFont( retVal.getFont().deriveFont(10.0f));
1191    return retVal;
1192  }
1193
1194
1195  /**  Inits the gui widgets - buttons, layouts, etc.**/
1196  private void initWindowPanels()
1197  {
1198    /*
1199     *  Init Window dressing
1200     */
1201    String miniViewFilename = "/images/miniViewButtonImage.jpg";
1202    String miniCloseFilename = "/images/miniCloseButtonImage.jpg";
1203    String x10DimFilename = "/images/Down24.gif";
1204    String x10BrightFilename = "/images/Up24.gif";
1205
1206    // the dragabler title bar button
1207    miniDragButton.setMaximumSize( new Dimension( 100, 16 ) );
1208    miniDragButton.setBorder( BorderFactory.createRaisedBevelBorder() );
1209    miniDragButton.setBorderPainted( true );
1210    miniDragButton.setDoubleBuffered( true );
1211    miniDragButton.add( miniDragLabel );
1212    miniDragButton.addMouseListener( miniViewMouseListener );
1213    miniDragButton.addMouseMotionListener( dragMiniViewMotionListener );
1214    miniDragButton.addMouseListener( popupListener_ );
1215
1216    // the mini close button
1217    iconMiniClose_ =
1218        new ImageIcon( Util.loadImage( miniCloseFilename ), "Close " + className_ );
1219    miniButtonClose_.addActionListener( this );
1220    miniButtonClose_.setIcon( iconMiniClose_ );
1221    miniButtonClose_.setToolTipText( "Close " + className_ );
1222    miniButtonClose_.setPreferredSize( new Dimension( 16, 16 ) );
1223    miniButtonClose_.setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
1224    miniButtonClose_.setBorderPainted( false );
1225    miniButtonClose_.setDoubleBuffered( true );
1226
1227    // the mini Dim button
1228    iconDim_ =
1229        new ImageIcon( Util.loadImage( x10DimFilename ), "dim" );
1230    dimButton_.addActionListener( this );
1231    dimButton_.setIcon( iconDim_ );
1232    dimButton_.setToolTipText( "dim" );
1233    dimButton_.setPreferredSize( new Dimension( 16, 16 ) );
1234    dimButton_.setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
1235    dimButton_.setBorderPainted( false );
1236    dimButton_.setDoubleBuffered( true );
1237
1238    // the mini Dim button
1239    iconBright_ =
1240        new ImageIcon( Util.loadImage( x10BrightFilename ), "bright" );
1241    brightButton_.addActionListener( this );
1242    brightButton_.setIcon( iconBright_ );
1243    brightButton_.setToolTipText( "bright" );
1244    brightButton_.setPreferredSize( new Dimension( 16, 16 ) );
1245    brightButton_.setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
1246    brightButton_.setBorderPainted( false );
1247    brightButton_.setDoubleBuffered( true );
1248
1249    // the Title Area Panel
1250    miniViewWindowControlPanel.add( miniDragButton, BorderLayout.CENTER );
1251    miniViewWindowControlPanel.add( miniButtonClose_, BorderLayout.EAST );
1252    miniViewWindowControlPanel.setDoubleBuffered( true );
1253    miniViewWindowControlPanel.addMouseListener( popupListener_ );
1254
1255    // the Button Panel
1256    miniButtonPanel_.setLayout( new GridLayout( displayRows_, displayCols_, 1, 1 ) );
1257    miniButtonPanel_.setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
1258    miniButtonPanel_.addMouseListener( popupListener_ );
1259    java.util.Arrays.sort(insteonResponderNames_);
1260    // Do all the NON-X10 ones first
1261    for ( int i = 0; i < numButtons_; i++ )
1262    {
1263      if (!insteonResponderNames_[i].startsWith("A"))
1264      {
1265        controlButton_[i] = createButtonFromName( insteonResponderNames_[i] );
1266        miniButtonPanel_.add( controlButton_[i] );
1267      }
1268    }
1269    // go through again and add the X10 ones
1270    for ( int i = 0; i < numButtons_; i++ )
1271    {
1272      if (insteonResponderNames_[i].startsWith("A"))
1273      {
1274        controlButton_[i] = createButtonFromName( insteonResponderNames_[i] );
1275        miniButtonPanel_.add( controlButton_[i] );
1276      }
1277    }
1278
1279    dimPanel_.setLayout( new GridLayout( 1, 2, 1, 1 ) );
1280    dimPanel_.setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
1281    //houseSpinner_.setValue( houseCodes_[0] );
1282    //onOffPanel_.add( houseSpinner_ );
1283    //onOffPanel_.add( onOffToggle_ );
1284    dimPanel_.add( brightButton_ );
1285    dimPanel_.add( dimButton_ );
1286
1287    // now stitch it all together
1288    dragableMiniViewPanel.setDoubleBuffered( true );
1289    dragableMiniViewPanel.setLayout( new BorderLayout( 0, 0 ) );
1290    dragableMiniViewPanel.add( miniViewWindowControlPanel, BorderLayout.NORTH );
1291    dragableMiniViewPanel.add( miniButtonPanel_, BorderLayout.CENTER );
1292    dragableMiniViewPanel.add( dimPanel_, BorderLayout.SOUTH );
1293    getContentPane().add( dragableMiniViewPanel );
1294  }
1295
1296
1297  /**
1298   *  Description of the Method
1299   *
1300   * @param  state  Description of the Parameter
1301   */
1302  private void enableDeviceButtons( boolean state )
1303  {
1304    for ( int i = 0; i < numButtons_; i++ )
1305    {
1306      controlButton_[i].setEnabled( state );
1307    }
1308  }
1309}
1310