001/*
002 *  $Source: $
003 *  $Name:  $
004 *  $Revision: 1032 $
005 *  $Date: 2015-11-08 19:15:20 -0800 (Sun, 08 Nov 2015) $
006 *  $Locker:  $
007 */
008/*
009 *  ISY99CLI -- A simple commandline access that turns on/off Insteon
010 *  devices and scenes..
011 *
012 *  Copyright (C) 2012 WebARTS Design, North Vancouver Canada
013 *  http://www.webarts.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
030//import ca.bc.webarts.widgets.Util;
031
032import java.io.File;
033import java.lang.StringBuilder;
034import java.net.URLEncoder;
035import java.util.Enumeration;
036import java.util.Iterator;
037import java.util.NoSuchElementException;
038import java.util.Set;
039import java.util.StringTokenizer;
040import java.util.Vector;
041
042import com.udi.insteon.client.InsteonConstants;
043import com.udi.isy.jsdk.insteon.ISYInsteonClient;
044import com.universaldevices.client.NoDeviceException;
045import com.universaldevices.common.UDUtil;
046import com.universaldevices.device.model.ProductInfo;
047import com.universaldevices.device.model.UDGroup;
048import com.universaldevices.device.model.UDNode;
049import com.universaldevices.resources.errormessages.Errors;
050import com.universaldevices.soap.*;
051import com.universaldevices.upnp.*;
052import com.universaldevices.rest.UDRestResponse;
053
054import com.nanoxml.XMLElement;
055
056/**
057 *  A simple wrapper for sending commands to an Insteon device on or off
058 *  via a Universal Devices ISy-99i. This app requires
059 *  a few other libraries: webarts  as well as the UDI JSDK
060 *  implementation on your system.
061 *
062 * @author    tgutwin
063 */
064public class ISY99CLI
065{
066  /*  A Class holder for its name (used in Logging).  */
067  private static String className_ = "ISY99CLI";
068
069  /**  A holder for this clients System File Separator.  */
070  public final static String SYSTEM_FILE_SEPERATOR = File.separator;
071
072  /**  A holder for this clients System line termination separator.  */
073  public final static String SYSTEM_LINE_SEPERATOR =
074                                           System.getProperty("line.separator");
075  private static IsyInsteonClient myISY =  null;
076  private boolean stillRunning_ = false;
077
078  private String[] insteonAddresses_ = {"14.2B.31.1"};
079  private String lastDevice_="";
080  private int numNodes_ = insteonAddresses_.length;
081
082
083  private Thread startupThread_ = new Thread ()
084    {
085      public void run()
086      {
087        try
088        {
089          myISY.start();
090        }
091        catch (Exception ex)
092        {
093          // nothing
094        }
095      }
096
097    };
098
099
100    public boolean stillRunning()
101    {
102      return this.stillRunning_;
103    }
104
105
106    public boolean startIsy( )
107    {
108      System.out.print(" **Starting Client...");
109      //startupThread_.start();
110      myISY.start();
111      UDUtil.sleep(1000);
112      int timeOut = 0;
113      //timeOut+=1000;
114      while (!myISY.isReadyToGo()  && timeOut < 10000)
115      {
116        UDUtil.sleep(200);
117        timeOut+=200;
118      }
119      if (timeOut>9999)
120        System.out.println(" Startup Timed Out.");
121      else
122        System.out.println(" Startup Successful.");
123      this.stillRunning_ = myISY.isReadyToGo();
124      UDUtil.sleep(2000);
125      return this.stillRunning_ ;
126    }
127
128
129    /**
130     * Cleans   up any ISY cleanup procedures in prep for exit.
131     */
132  protected  void isyCleanup()
133  {
134    System.out.print("\nCleaning up before Exit... ");
135    try
136    {
137      startupThread_ = null;
138      this.stillRunning_ = false;
139      UDUtil.sleep(250);
140      myISY.unsubscribeFromEvents();
141      myISY.cleanUp();
142      myISY.stop();
143      //startupThread_.stop();
144      //Util.sleep(5000);
145      //if (myISY.isReadyToGo()) { System.out.println(" waiting...");UDUtil.sleep(2000);}
146      
147      Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
148      Thread currThread = null;
149      Iterator iter = threadSet.iterator();
150      while (iter.hasNext()) 
151      {
152        currThread = (Thread) iter.next();
153        System.out.print(currThread.getName());
154        try
155        {
156          if (currThread.getName().startsWith("Thread")) 
157          {
158                      System.out.print("[X]");
159                      currThread.interrupt();
160          }
161          System.out.print(" ");
162        }
163         catch (Exception ex)
164        {
165          // nothing
166        }
167      }
168    }
169    catch (Exception ex)
170    {
171      // nothing
172    }
173    System.out.println("done!");
174
175  }
176
177
178
179    /**
180     * Notifies the user of a syntax error
181     */
182  protected void syntaxError()
183  {
184    System.err.println("Syntax error. Try again.");
185  }
186
187
188
189  /**  Constructor * */
190  public ISY99CLI()
191  {
192    myISY = new IsyInsteonClient();
193
194  }
195
196
197  /**
198   *  The main program for the ISY99Buttons class.
199   *
200   * @param  arg  The command line arguments
201   */
202  public static void main( String[] arg )
203  {
204    System.out.println( "Starting "+className_ );
205    if (arg.length==0)
206    {
207      System.out.println("");
208      System.out.println("SYNTAX:  java ca.bc.webarts.tools.isy.ISY99CLI  [command] [DeviceName]");
209      System.out.println("        command can be one of:\n         dump, status, dim, bright, toggle, on, off");
210      System.out.println("        DeviceName  is the name of the device for the command (NOT the address)");
211      System.out.println("        dump  will dump out all the characteristics of the ISY as well as the connected Devices");
212    }
213    else
214    {
215      ISY99CLI instance = new ISY99CLI();
216      Errors.addErrorListener(new MyISYErrorHandler());
217
218      String command = "";
219      String device = "";
220      String cmdOption = "";
221
222      try
223      {
224        //System.out.println("starting ISY");
225        instance.startIsy(); // this starts and BLOCKS until started
226        //System.out.println("waiting for the ISY to settle ...");
227        //Thread.sleep(500);
228        int timeOut = 0;
229        //timeOut+=1000;
230        while (!instance.stillRunning_  && timeOut < 10000)
231        {
232          UDUtil.sleep(200);
233          timeOut+=200;
234        }
235        if (timeOut>9999) System.out.print("\n*!*Startup Timed Out.");
236
237        //System.out.println("continuing...");
238        if (instance.stillRunning_  && instance.myISY.isReadyToGo())
239        {
240          instance.loadNodeAddresses();
241          // System.out.println(dumpIsyProductInfo(isyProductInfo).toString());
242          //instance.startIsy(); // this starts and BLOCKS until started
243            System.out.println("Commanded to do: "+arg[0]);
244            command = arg[0];
245            if(!command.equals("dump")) device = arg[1];
246
247            //System.out.println("Action From: "+command);
248            boolean dim = command.trim().startsWith("dim");
249            boolean bright = command.trim().startsWith("bright");
250
251            /* *********************************** */
252            if(command.equals("dump"))
253            {
254              //com.universaldevices.upnp.UDProxyDevice isyDevice = instance.myISY.getDevice();
255              //ProductInfo isyProductInfo = isyDevice.getProductInfo();
256              System.out.println(instance.dumpIsyProductInfo().toString());
257              System.out.println("  Switchable Nodes");
258              System.out.println("  ~~~~~~~~~~~~~~~~");
259              for (int i=0; i< instance.insteonAddresses_.length; i++)
260              {
261                System.out.println("    "+ instance.getNodeName(instance.insteonAddresses_[i])+" ("+instance.insteonAddresses_[i]+")");
262              }
263
264              System.out.println(instance.dumpVariablesInfo().toString());
265              System.out.println(instance.dumpElkInfo().toString());
266            }
267
268            /* *********************************** */
269            else if (dim || bright)
270            {
271              String deviceAddress = instance.getNodeAddress(device);
272                if (dim)
273                  {
274                    //myISY.dimDevice(lastDevice_);
275                    instance.myISY.dimDevice(deviceAddress.replace("."," "));
276                  }
277                else if (bright)
278                  {
279                    //myISY.brightenDevice(lastDevice_);
280                    instance.myISY.brightenDevice(deviceAddress.replace("."," "));
281                  }
282
283            }
284
285            /* *********************************** */
286            else if(command.equals("toggle"))
287            {
288              String deviceAddress = instance.getNodeAddress(device);
289
290              System.out.print("Toggle: "+device+ " ("+deviceAddress+") " );
291              {
292                String deviceStatus = instance.getStatus(instance.getNode(deviceAddress));
293                // System.out.println("  to turn  "+(onOffToggle_.getState()?"On":"Off") );
294                if (deviceStatus!=null) System.out.println("  to turn  "+(deviceStatus.equals("0")?"On":"Off") );
295                System.out.println(" Device Status: "+deviceStatus);
296                //processCommand( onOffCommand+" "+Util.tokenReplace(deviceAddress," ",".") );
297
298                //if (onOffToggle_.getState())
299                if (deviceStatus==null||deviceStatus.equals("0"))
300                  {
301                    //myISY.turnDeviceOn(deviceAddress);
302                    instance.myISY.turnDeviceOn(deviceAddress.replace("."," "));
303                  }
304                else
305                  {
306                    //myISY.turnDeviceOff(deviceAddress);
307                    instance.myISY.turnDeviceOff(deviceAddress.replace("."," "));
308                  }
309              }
310            }
311
312            /* *********************************** */
313            else if(command.equals("on")||command.equals("off"))
314            {
315              String deviceAddress = instance.getNodeAddress(device);
316
317              if (command.equals("on"))
318              {
319                //myISY.turnDeviceOn(deviceAddress);
320                instance.myISY.turnDeviceOn(deviceAddress.replace("."," "));
321              }
322              else
323              {
324                //myISY.turnDeviceOff(deviceAddress);
325                instance.myISY.turnDeviceOff(deviceAddress.replace("."," "));
326              }
327              UDUtil.sleep(1000);
328              String deviceStatus = instance.getStatus(instance.getNode(deviceAddress));
329              if (deviceStatus!=null) System.out.println(device + " is now "+(deviceStatus.equals("0")?"Off":"On") );
330            }
331
332            /* *********************************** */
333            else if(command.equalsIgnoreCase("status"))
334            {
335              String deviceAddress = instance.getNodeAddress(device);
336              System.out.println(" Device : "+device+" "+deviceAddress);
337              //UDUtil.sleep(500);
338              String deviceStatus = instance.getStatus(instance.getNode(deviceAddress));
339              if (deviceStatus!=null) System.out.println(" STATUS: " + device + " is now "+(deviceStatus.equals("0")?"Off":"On") );
340              //UDUtil.sleep(500);
341              UDNode udn = instance.getNode(deviceAddress);
342              
343              
344              deviceStatus = instance.getiMeterPower(udn);
345              if (deviceStatus!=null && !deviceStatus.equals("") ) System.out.println(" iMeter Power: " + device + " is now "+deviceStatus+" Watts" );
346              //UDUtil.sleep(500);
347              deviceStatus = instance.getiMeterEnergy(udn);
348              if (deviceStatus!=null && !deviceStatus.equals("") ) System.out.println(" iMeter Energy: " + device + " is now "+deviceStatus+" Whr" );
349            }
350
351        }
352      }
353      catch (Exception e)
354      {
355        e.printStackTrace();
356      }
357      finally
358      {
359        instance.isyCleanup();
360        int timeOut = 0;
361        System.out.print("Waiting To Shutdown CLI...");
362        UDUtil.sleep(250);
363        timeOut+=250;
364        while (instance.stillRunning() && timeOut < 3000)
365        {
366          System.out.print(".");
367          UDUtil.sleep(250);
368          timeOut+=250;
369        }
370        System.out.println("!");
371      }
372    }
373  }
374
375
376  public StringBuilder dumpVariablesInfo() { return dumpVariablesInfo(this.myISY);}
377
378
379  public static StringBuilder dumpVariablesInfo(ISYInsteonClient isyIClient)
380  {
381    StringBuilder retVal = new StringBuilder("\n");
382
383    if (isyIClient!=null)
384    {
385      UDProxyDevice isyDevice = isyIClient.getDevice();
386      ProductInfo prod = isyDevice.getProductInfo();
387
388      if (prod!=null && prod.isVariablesEnabled())
389      {
390        String vars = isyDevice.getVariables(1); //VarType...  1=Integer Variable 2=State Variable
391        retVal.append("  Variables Information\n");
392        retVal.append("  ~~~~~~~~~~~~~~~~~~~~~~\n\n");
393        if (vars!=null) retVal.append("  Integers\n"+vars+"\n");
394        vars = isyDevice.getVariables(2); //VarType...  1=Integer Variable 2=State Variable
395        if (vars!=null) retVal.append("  StateVars\n"+vars+"\n");
396      }
397    }
398    return retVal;
399  }
400  
401
402  public StringBuilder dumpElkInfo() { return dumpElkInfo(this.myISY);}
403
404  public static StringBuilder dumpElkInfo(ISYInsteonClient isyIClient)
405  {
406    StringBuilder retVal = new StringBuilder("\n");
407
408    if (isyIClient!=null)
409    {
410      UDProxyDevice isyDevice = isyIClient.getDevice();
411      ProductInfo prod = isyDevice.getProductInfo();
412
413      if (prod!=null && prod.isElkEnabled())
414      {
415        retVal.append("  ELK Module Information\n");
416        retVal.append("  ~~~~~~~~~~~~~~~~~~~~~~\n\n");
417        String elkRest = "/rest/elk";
418        String elkCmd = "/system/get/status";
419
420        retVal.append("  ELK Rest Command: "+elkRest+elkCmd+"\n");
421        retVal.append("  ELK Rest Response: \n");
422        UDRestResponse res = isyDevice.submitRESTRequest(elkRest+elkCmd);
423        //UDRestResponse has 3 public methods
424        //              String getBody()
425        //              int getStatus()   - returns an HTTP status code
426        //              boolean isSucceeded()
427        if (res!=null && res.getBody()!=null && res.isSucceeded())
428        {
429          retVal.append(res.getBody().replace("><",">\n<"));
430        }
431        else if (res!=null) retVal.append("ELK Rest ERROR Response:  http status="+res.getStatus() + "   success:"+res.isSucceeded());
432
433        elkCmd = "/get/status";
434        retVal.append("\n\n  ELK Rest Command: "+elkRest+elkCmd+"\n");
435        retVal.append("  ELK Rest Response: \n");
436        res = isyDevice.submitRESTRequest(elkRest+elkCmd);
437        if (res!=null && res.getBody()!=null && res.isSucceeded())
438        {
439          try
440          {
441            XMLElement xmlelement = new XMLElement();
442            xmlelement.parseString(res.getBody());
443            boolean bool = true;
444            Enumeration enumeration = xmlelement.getChildren().elements();
445            while (enumeration.hasMoreElements())
446            {
447              XMLElement xmlElement  = (XMLElement) enumeration.nextElement();
448              if (xmlElement.getTagName().equals("ae"))
449              {
450                retVal.append(xmlElement.toString());
451              }
452              else if (xmlElement.getTagName().equals("ze"))
453                retVal.append(xmlElement.toString());
454              else if (xmlElement.getTagName().equals("ke"))
455                retVal.append(xmlElement.toString());
456              else if (xmlElement.getTagName().equals("oe"))
457                retVal.append(xmlElement.toString());
458              else if (xmlElement.getTagName().equals("se"))
459                retVal.append(xmlElement.toString());
460            }
461          }
462          catch (Exception exception)
463          {
464              /* empty */
465          }
466          //retVal.append(Util.tokenReplace(res.getBody(),"><",">\n<"));
467        }
468        else if (res!=null) retVal.append("ELK Rest ERROR Response:  http status="+res.getStatus() + "   success:"+res.isSucceeded());
469
470      }
471      else
472      {
473        retVal.append(" ELK Module NOT INSTALLED");
474      }
475    }
476
477    return retVal;
478  }
479
480    
481  public StringBuilder dumpIsyProductInfo() { return dumpIsyProductInfo(this.myISY);}
482
483
484  public static StringBuilder dumpIsyProductInfo(ISYInsteonClient isyIClient)
485  {
486    StringBuilder retVal = new StringBuilder("\n=================\nISY Product Info\n=================\n");
487
488    if (isyIClient!=null)
489    {
490      com.universaldevices.upnp.UDProxyDevice isyDevice = isyIClient.getDevice();
491      ProductInfo prod = isyDevice.getProductInfo(); 
492
493      if (prod!=null)
494      {
495        retVal.append("   - ");
496        retVal.append(isyDevice.uuid);
497        retVal.append(SYSTEM_LINE_SEPERATOR);
498        retVal.append("   - Description: ");
499        retVal.append(prod.getDesc());
500        retVal.append((prod.isIrEnabled()?" with IR enabled":" without IR enabled"));
501
502        retVal.append(SYSTEM_LINE_SEPERATOR);
503        retVal.append("   - ProductId: ");
504        retVal.append(isyDevice.getProductId());
505        retVal.append(SYSTEM_LINE_SEPERATOR);
506
507        retVal.append("   - Make / model ");
508        retVal.append(isyDevice.getMake());
509        retVal.append(" / " );
510        retVal.append(isyDevice.getModel());
511        retVal.append(SYSTEM_LINE_SEPERATOR);
512
513        retVal.append("   - Platform: ");
514        retVal.append(isyDevice.getPlatform());
515        retVal.append(SYSTEM_LINE_SEPERATOR);
516
517        retVal.append("   - Application & Version: ");
518        retVal.append(isyDevice.getApplication());
519        retVal.append(" - " );
520        retVal.append(isyDevice.getAppVersion());
521        retVal.append(" build: " );
522        retVal.append(isyDevice.getBuildTimestamp() );
523        retVal.append(SYSTEM_LINE_SEPERATOR);
524
525        retVal.append("   - SSL Certificate URL: ");
526        retVal.append(prod.getSSLCertificateURL());
527        retVal.append(SYSTEM_LINE_SEPERATOR);
528
529        retVal.append(SYSTEM_LINE_SEPERATOR);
530
531        retVal.append("  Network Configuration");
532        retVal.append(SYSTEM_LINE_SEPERATOR);
533        retVal.append("  ~~~~~~~~~~~~~~~~");
534        retVal.append(SYSTEM_LINE_SEPERATOR);
535        retVal.append("   - IP: " );
536        retVal.append(isyDevice.getNetworkConfig().ip);
537        retVal.append(SYSTEM_LINE_SEPERATOR);
538        retVal.append("   - " );
539        retVal.append(isyDevice.getNetworkConfig().toString().replace("<br>","\n   - "));
540        retVal.append("Gateway: " );
541        retVal.append(isyDevice.getNetworkConfig().gateway);
542        retVal.append(SYSTEM_LINE_SEPERATOR);
543        retVal.append("   - DNS: " );
544        retVal.append(isyDevice.getNetworkConfig().dns);
545        retVal.append(SYSTEM_LINE_SEPERATOR);
546        retVal.append(SYSTEM_LINE_SEPERATOR);
547
548        retVal.append("  File System Status");
549        retVal.append(SYSTEM_LINE_SEPERATOR);
550        retVal.append("  ~~~~~~~~~~~~~~~~");
551        retVal.append(SYSTEM_LINE_SEPERATOR);
552
553        com.universaldevices.device.model.FileSystemStat fss = isyDevice.getFileSystemStatus();
554        retVal.append("   - Active: ");
555        retVal.append((fss.isActive()?"YES":"NO"));
556        retVal.append(SYSTEM_LINE_SEPERATOR);
557
558        retVal.append("   - Total: ");
559        retVal.append(fss.getTotal());
560        retVal.append(SYSTEM_LINE_SEPERATOR);
561
562        retVal.append("   - Free: ");
563        retVal.append(fss.getFree());
564        retVal.append(SYSTEM_LINE_SEPERATOR);
565
566        retVal.append("   - Reserved: ");
567        retVal.append(fss.getReserved());
568        retVal.append(SYSTEM_LINE_SEPERATOR);
569
570        retVal.append("   - Bad: ");
571        retVal.append(fss.getBad());
572        retVal.append(SYSTEM_LINE_SEPERATOR);
573
574        retVal.append(SYSTEM_LINE_SEPERATOR);
575
576        retVal.append("  Enabled Features");
577        retVal.append(SYSTEM_LINE_SEPERATOR);
578        retVal.append("  ~~~~~~~~~~~~~~~~");
579        retVal.append(SYSTEM_LINE_SEPERATOR);
580
581        retVal.append("   - D2d: ");
582        retVal.append((prod.isD2dEnabled()?"YES":"NO"));
583        retVal.append(SYSTEM_LINE_SEPERATOR);
584
585        retVal.append("   - Zigbee SEP Device: ");
586        retVal.append((prod.isZigbeeSEPDeviceEnabled()?"YES":"NO"));
587        retVal.append(SYSTEM_LINE_SEPERATOR);
588
589        retVal.append("   - BroadBand SEP Device: ");
590        retVal.append((prod.isBroadBandSEPDeviceEnabled()?"YES":"NO"));
591        retVal.append(SYSTEM_LINE_SEPERATOR);
592
593        retVal.append("   - Any SEP Device: ");
594        retVal.append((prod.isAnySEPDeviceEnabled()?"YES":"NO"));
595        retVal.append(SYSTEM_LINE_SEPERATOR);
596
597        retVal.append("   - Open DR: ");
598        retVal.append((prod.isOpenDREnabled()?"YES":"NO"));
599        retVal.append(SYSTEM_LINE_SEPERATOR);
600
601        retVal.append("   - URL Access: ");
602        retVal.append((prod.isURLAccessEnabled()?"YES":"NO"));
603        retVal.append(SYSTEM_LINE_SEPERATOR);
604
605        retVal.append("   - Variables Enabled: ");
606        retVal.append((prod.isVariablesEnabled()?"YES":"NO"));
607        retVal.append(SYSTEM_LINE_SEPERATOR);
608
609        retVal.append("   - ElectricMeter Enabled: ");
610        retVal.append((prod.isElectricityMeterEnabled() ?"YES":"NO"));
611        retVal.append(SYSTEM_LINE_SEPERATOR);
612        retVal.append(SYSTEM_LINE_SEPERATOR);
613
614        retVal.append("  Modules");
615        retVal.append(SYSTEM_LINE_SEPERATOR);
616        retVal.append("  ~~~~~~~");
617        retVal.append(SYSTEM_LINE_SEPERATOR);
618
619        retVal.append("   - Web: ");
620        retVal.append((prod.isWebModulesEnabled()?"YES":"NO"));
621        retVal.append(SYSTEM_LINE_SEPERATOR);
622
623        retVal.append("   - Weather: ");
624        retVal.append((prod.isWeatherEnabled()?"YES":"NO"));
625        retVal.append(SYSTEM_LINE_SEPERATOR);
626
627        retVal.append("   - A10: ");
628        retVal.append((prod.isInsteonA10Enabled()?"YES":"NO"));
629        retVal.append(SYSTEM_LINE_SEPERATOR);
630
631        retVal.append("   - Energy Monitoring: ");
632        retVal.append((prod.isEnergyMonitoringEnabled()?"YES":"NO"));
633        retVal.append(SYSTEM_LINE_SEPERATOR);
634
635        retVal.append("   - ELK Security: ");
636        retVal.append((prod.isElkEnabled()?"YES":"NO"));
637        retVal.append(SYSTEM_LINE_SEPERATOR);
638
639        retVal.append("   - Energy Monitoring: ");
640        retVal.append((prod.isEnergyMonitoringEnabled()?"YES":"NO"));
641        retVal.append(SYSTEM_LINE_SEPERATOR);
642
643        retVal.append("   - Energy Monitoring: ");
644        retVal.append((prod.isEnergyMonitoringEnabled()?"YES":"NO"));
645        retVal.append(SYSTEM_LINE_SEPERATOR);
646      }
647    }
648    else
649    {
650      retVal.append(SYSTEM_LINE_SEPERATOR);
651      retVal.append("ERROR: No IsyInsteonClient");
652      retVal.append(SYSTEM_LINE_SEPERATOR);
653    }
654
655    return retVal;
656  }
657
658
659    /**
660     * Returns all <code>UDNode</code>s that are associated in the client.
661     * @return the  Enumeration UDNode if found, null otherwise
662     */
663  protected Enumeration <UDNode> getNodes()// throws com.universaldevices.client.NoDeviceException
664  {
665    Enumeration <UDNode> isyNodes = null;
666    try
667    {
668      isyNodes = myISY.getNodes().elements(); // UDClient.getNodes
669    }
670    catch (Exception ex)  // probably com.universaldevices.client.NoDeviceException
671    {
672      // send empty
673    }
674    return isyNodes;
675
676    //return myISY.getNodes().elements();
677  }
678
679
680    /**
681     * Returns all NON-ControlLinc or Non-SwitchLinc <code>UDNode</code>s that are associated in the client.
682     * @return the  Enumeration UDNode if found, null otherwise
683     */
684  protected Enumeration <UDNode> getMyNodes()// throws com.universaldevices.client.NoDeviceException
685  {
686    Enumeration <UDNode> isyNodes = null;
687    Vector <UDNode> myNodes =new Vector<UDNode>();
688
689    try
690    {
691      isyNodes = myISY.getNodes().elements();
692      UDNode node = null;
693
694      while ( isyNodes.hasMoreElements())
695      {
696        node = (UDNode) isyNodes.nextElement();
697        // System.out.println(" Check To Add "+node.address + ":" + node.name+" type="+node.typeReadable+"  -->"+ !node.typeReadable.startsWith("00."));
698        if (!node.typeReadable.startsWith("00.00") && /* ContolLinc */
699            !node.typeReadable.startsWith("00.05") && /* RemoteLinc */
700            !node.typeReadable.startsWith("01.09")   /* SwitchLinc */ )
701          myNodes.add(node);
702      }
703    }
704    catch (Exception ex)  // probably com.universaldevices.client.NoDeviceException
705    {
706      // send empty
707    }
708    return myNodes.elements();
709  }
710
711
712  /**
713   * Counts all the NON-ContolLinc Nodes that the ISY is aware of.
714   * @return the number of  NON-ContolLinc nodes  that the ISY is aware of.
715   **/
716  private int countNodes()
717  {
718    int retVal = 0;
719    try
720    {
721      Enumeration <UDNode> e = getNodes();
722      UDNode node = null;
723      while ( e.hasMoreElements())
724      {
725        node = e.nextElement();
726        retVal++;
727      }
728    }
729    catch (Exception ex)
730    {
731      ex.printStackTrace();
732    }
733    return retVal;
734  }
735
736
737  /**
738   * Counts all the NON-ContolLinc Nodes that the ISY is aware of.
739   * @return the number of  NON-ContolLinc nodes  that the ISY is aware of.
740   **/
741  private int countMyNodes()
742  {
743    int retVal = 0;
744    try
745    {
746      Enumeration <UDNode> e = getMyNodes();
747      UDNode node = null;
748      while ( e.hasMoreElements())
749      {
750        node = e.nextElement();
751        retVal++;
752      }
753    }
754    catch (Exception ex)
755    {
756      ex.printStackTrace();
757    }
758    return retVal;
759  }
760
761
762  /**
763   * Puts all the Node Address at this ISY into the insteonAddresses_ array so they can get assigned to buttons.
764   **/
765  private void loadNodeAddresses()
766  {
767    try
768    {
769      int numNodes = countMyNodes();
770      insteonAddresses_ = new String[numNodes];
771      System.out.println("Loading "+numNodes+" Controllable Devices");
772      int count = 0;
773      Enumeration <UDNode> e = getMyNodes();
774      while (e.hasMoreElements())
775      {
776        UDNode node = e.nextElement();
777        insteonAddresses_[count++] = node.address.replace(" ",".");
778        //System.out.println("                 "+node.address + ":" + node.name+" type="+node.typeReadable);
779      }
780      numNodes_ = numNodes;
781    }
782    catch (Exception ex)
783    {
784      ex.printStackTrace();
785    }
786
787  }
788
789
790    /**
791     * Returns the current value of Insteon Devicse (its state)
792     * @param tk - the StringTokenzier
793     */
794  protected void processStatus(StringTokenizer tk) 
795  {
796    String tmp = tk.nextToken();        //device address
797    UDNode node = getNode(tmp);
798    if (node == null) {
799      return;
800    }
801
802    tmp = (String) myISY.getCurrValue(node, InsteonConstants.DEVICE_STATUS);
803    System.out.println("The current status for " + node.address + "/" + node.name + " is " + tmp);
804  }
805
806
807    /**
808     * Returns the current value of an iMeters Energy Level (wattMins)
809     * @param node - the UDNode to query
810     * @return the energy level in WattHrs
811     **/
812  protected String getiMeterEnergy(UDNode node ) 
813  {
814    String retVal = "";
815    if (!(node==null || !node.typeReadable.equals("09.07"))) // iMeter Device Type
816    {
817      //System.out.println("iMeter type="+ node.typeReadable);
818     // myISY.queryNode( node.address); // do an update
819      retVal = (String) myISY.getCurrValue(node, InsteonConstants.TOTAL_POWER); // This returns Watt Seconds
820      retVal = ""+(Integer.parseInt(retVal)*10/36);
821      //system.out.println("The current status for " + node.address + "/" + node.name + " is " + retVal);
822    }
823    return retVal;
824  }
825
826  
827    /**
828     * Returns the current value of an iMeters Power Level (Watts
829     * @param node - the UDNode to query
830     * @return the power level in Watts
831     **/
832  protected String getiMeterPower(UDNode node ) 
833  {
834    StringBuilder retVal = new StringBuilder("");
835    if (!(node==null || !node.typeReadable.equals("09.07"))) // iMeter Device Type
836    {
837      System.out.println(" iMeter type: "+ node.typeReadable); // + " [" +node.type + "] (UDI DeviceType for a iMeter="+com.udi.insteon.client.DeviceTypes.DEV_SCAT_IMETER_SOLO +") ");
838      myISY.queryNode( node.address); // do an update
839      String ret = (String) myISY.getCurrValue(node, InsteonConstants.CURRENT_POWER);
840      
841      if (ret != null && !ret.equals("")&& !ret.equals("null")) 
842      {
843        retVal.append(ret);
844        System.out.println("  POWER reading="+ ret);
845      }
846      else
847      {
848        retVal = new StringBuilder("");
849        //system.out.println("The current status for " + node.address + "/" + node.name + " is " + retVal);
850        
851        // Now try the rest response
852        UDProxyDevice isyDevice = myISY.getDevice();
853        String prop = "ST";
854        
855        //retVal.append("\n  ISY Rest Command: "+restNodePropertyCommandString(prop, node) +"\n");
856        //retVal.append("\n  ISY Rest Response: ");
857        UDRestResponse res = isyDevice.submitRESTRequest(restNodePropertyCommandString(prop, node));
858        if (res!=null && res.getBody()!=null && res.isSucceeded())
859        {
860          try
861          {
862            XMLElement xmlelement = new XMLElement();
863            xmlelement.parseString(res.getBody());
864            //boolean bool = true;
865            Enumeration enumeration = xmlelement.getChildren().elements();
866            while (enumeration.hasMoreElements())
867            {
868              XMLElement xmlElement  = (XMLElement) enumeration.nextElement();
869              double defaultDbl = 0.0;
870              retVal.append(""+xmlElement.getProperty("VALUE",defaultDbl));
871              if (enumeration.hasMoreElements()) retVal.append("\n");
872            }
873          }
874          catch (Exception exception)
875          {
876              /* empty */
877          }
878          //retVal.append(Util.tokenReplace(res.getBody(),"><",">\n<"));
879        }
880        else if (res!=null) retVal.append("ISY Rest ERROR Response:  http status="+res.getStatus() + "   success:"+res.isSucceeded());
881      }
882    }
883    return retVal.toString();
884  }
885
886  /** Strings together a rest 'noded' service string (with the spaces encoded) for the given node. 
887   *
888   * @param node the node to query for (null to query all)
889   * @return the rest service string with the spaces encoded (/rest/node/12%2089%2044%201)
890   **/
891  public String restNodePropertyCommandString(String propName, UDNode node)
892  {
893    return restNodesCommandString(node)+"/"+ propName;
894  }
895 
896  /** Strings together a rest 'noded' service string (with the spaces encoded) for the given node. 
897   *
898   * @param node the node to query for (null to query all)
899   * @return the rest service string with the spaces encoded (/rest/node/12%2089%2044%201)
900   **/
901  public String restNodesCommandString(UDNode node)
902  {
903    return restCommandString("nodes", node);
904  }
905 
906  
907  /** Strings together a rest 'status' service string (with the spaces encoded) for the given node. 
908   *
909   * @param node the node to query for (null to query all)
910   * @return the rest service string with the spaces encoded (/rest/node/12%2089%2044%201)
911   **/
912  public String restStatusCommandString(UDNode node)
913  {
914    return restCommandString("status", node);
915  }
916 
917  
918  /** Strings together a rest 'query' service string (with the spaces encoded) for the given node. 
919   *
920   * @param node the node to query for (null to query all)
921   * @return the rest service string with the spaces encoded (/rest/node/12%2089%2044%201)
922   **/
923  public String restQueryCommandString(UDNode node)
924  {
925    return restCommandString("query", node);
926  }
927 
928  
929  /** Strings together a rest service string (with the spaces encoded) for the givven node. 
930   *
931   * @param node the node to query for (null to query all)
932   * @return the rest service string with the spaces encoded (/rest/node/12%2089%2044%201)
933   **/
934  public String restCommandString(String command, UDNode node)
935  {
936    String retVal = "/rest/";
937    retVal+= command;
938    if (node!=null)
939    {
940      String nodeAddr = getNodeAddress(node.name);
941      retVal += "/"+ node.address.replace(" ","%20");
942    }
943    return retVal;
944  }
945 
946  
947    /**
948     * Returns the current value of an Insteon Device (its state)
949     * @param node - the UDNode to query
950     * @return the status
951     **/
952  protected String getStatus(UDNode node ) {
953    if (node == null) {
954      return "";
955    }
956
957    String tmp = (String) myISY.getCurrValue(node, InsteonConstants.DEVICE_STATUS);
958    //stem.out.println("The current status for " + node.address + "/" + node.name + " is " + tmp);
959
960    return tmp;
961  }
962
963
964  /** Duh!
965   * @param the node's address to query.
966   * @return the name assigned to the node at the address requested.
967   **/
968  protected String getNodeName(String address)
969  {
970    if (address == null)
971    {
972      System.err.println("Missing Device/Scene address");
973      return "";
974    }
975    UDNode node = getNode(address);
976    return node.name;
977  }
978
979
980  /** Duh!
981   * @param the node's address to query.
982   * @return the name assigned to the node at the address requested.
983   **/
984  protected String getNodeAddress(String nodeName)
985  {
986    String retVal ="";
987    UDNode node = null;
988    if (nodeName == null)
989    {
990      System.err.println("Missing Device/Scene address");
991      return "";
992    }
993    for (int i=0; i< insteonAddresses_.length; i++)
994    {
995      node = getNode(insteonAddresses_[i]);
996      if(node.name.equals(nodeName))
997      {
998        retVal = node.address;
999        retVal = retVal.replace(" ",".");
1000      }
1001    }
1002    //retVal.replace(" ",".");
1003    return retVal;
1004  }
1005
1006
1007    /**
1008     * Returns a <code>UDGroup</code> or a <code>UDNode</code> based on the
1009     * given address
1010     * @param address - the address of the node/scene to be retrieved
1011     * @return the UDNode if found, null otherwise
1012     */
1013  protected UDNode getNode(String address)
1014  {
1015    if (address == null)
1016    {
1017      System.err.println("Missing Device/Scene address");
1018      return null;
1019    }
1020    if (address.indexOf(".") > 0)
1021    {
1022            //this is an insteon device
1023      address = address.replace(".", " ");            //normalize it to our format
1024
1025      try
1026      {
1027        UDNode node = myISY.getNodes().get(address);
1028        if (node == null)
1029        {
1030          System.err.println("Address points to a non existing Insteon Device");
1031          return null;
1032        }
1033        return node;
1034      }
1035      catch (NoDeviceException e)
1036      {
1037        System.out.println(e);
1038        return null;
1039      }
1040    }
1041
1042    //this is an insteon scene
1043    try
1044    {
1045      UDGroup group = myISY.getGroups().get(address);
1046      if (group == null)
1047      {
1048        System.err.println("Address points to a non-existing scene");
1049        return null;
1050      }
1051      return group;
1052    }
1053    catch (Exception e)
1054    {
1055      System.err.println(e);
1056      return null;
1057    }
1058  }
1059
1060  protected char INSTEON_MASTER_MODE = 0xF0;
1061  protected char INSTEON_SLAVE_MODE = 0x0F;
1062
1063    /**
1064     * Returns the mode based on the input
1065     * @param mode
1066     * @return - the mode (INSTEON_MASTER_MODE, INSTEON_SLAVE_MODE)
1067     */
1068  protected char getMode(String mode)
1069  {
1070    if (mode.equals("M"))
1071    {
1072      return INSTEON_MASTER_MODE;
1073    }
1074
1075    if (mode.equals("S"))
1076    {
1077      return INSTEON_SLAVE_MODE;
1078    }
1079
1080    System.err.println("Please specify M (for Master mode) or S (for Slave mode)");
1081    return 0;
1082  }
1083
1084    /**
1085     * Processes an Insteon command
1086     * @param cmd - the command to be processed
1087     * @param tk - the StringTokenizer
1088     */
1089  protected void processInsteonCommand(String cmd, StringTokenizer tk)
1090  {
1091    String address = tk.nextToken();
1092    System.out.println("tk addr:"+address);
1093    UDNode node = getNode(address);
1094    if (node == null)
1095    {
1096      return;
1097    }
1098
1099        //it's a group
1100    if (node instanceof UDGroup)
1101    {
1102      myISY.changeGroupState(cmd, null, node.address);
1103    }
1104    else
1105    {
1106      myISY.changeNodeState(cmd, null, node.address);
1107    }
1108  }
1109
1110  /**
1111   *  Abstracts the actual sending of the ISY Command.
1112   *
1113   * @param  command       This apps Command (not necessarily an Insteon Command (but could Be)
1114   */
1115  public void processCommand( String command)
1116  {
1117    if (command == null || command.length() < 1) 
1118    {
1119      return;
1120    }
1121
1122    System.out.println("Button Command:"+command);
1123    StringTokenizer tk = new StringTokenizer(command, " ");
1124    try
1125    {
1126      String cmd = tk.nextToken();
1127      if (  cmd.startsWith(InsteonConstants.DEVICE_ON) ||
1128            cmd.startsWith(InsteonConstants.DEVICE_OFF) ||
1129            cmd.startsWith(InsteonConstants.DEVICE_FAST_ON) ||
1130            cmd.startsWith(InsteonConstants.DEVICE_FAST_OFF) ||
1131            cmd.startsWith(InsteonConstants.LIGHT_DIM) ||
1132            cmd.startsWith(InsteonConstants.LIGHT_BRIGHT))
1133      {
1134        System.out.println("Processing Cmd:"+cmd+" tk:"+tk.toString());
1135        processInsteonCommand(cmd, tk);
1136      }
1137      else if (cmd.equalsIgnoreCase("StartLinking"))
1138      {
1139         myISY.startLinking();
1140      }
1141      else if (cmd.equalsIgnoreCase("StopLinking"))
1142      {
1143         myISY.stopLinking();
1144      }
1145      else if (cmd.startsWith("SetLinkingMode"))
1146      {
1147        //setLinkingMode(tk);
1148      }
1149      else if (cmd.startsWith("Rename"))
1150      {
1151        //processRename(tk);
1152      }
1153      else if (cmd.startsWith("Delete"))
1154      {
1155       // processDelete(tk);
1156      }
1157      else if (cmd.startsWith("Remove"))
1158      {
1159        //processRemoveFromScene(tk);
1160      }
1161      else if (cmd.startsWith("Move"))
1162      {
1163       // processMove(tk);
1164      }
1165      else if (cmd.startsWith("NewScene"))
1166      {
1167       // processNewScene(tk);
1168      }
1169      else if (cmd.startsWith("SetSceneOnLevel"))
1170      {
1171        //processSceneOnLevel(tk);
1172      }
1173      else if (cmd.startsWith("SetSceneRampRate"))
1174      {
1175       // processSceneRampRate(tk);
1176      }
1177      else if (cmd.startsWith("SetSceneControllerOnLevel"))
1178      {
1179       // processSceneControllerOnLevel(tk);
1180      }
1181      else if (cmd.startsWith("SetSceneControllerRampRate"))
1182      {
1183        //processSceneControllerRampRate(tk);
1184      }
1185      else if (cmd.equalsIgnoreCase("ListNodes"))
1186      {
1187       // processListNodes();
1188      }
1189      else if (cmd.equalsIgnoreCase("ListScenes"))
1190      {
1191       // processListScenes();
1192      }
1193      else if (cmd.startsWith("GetStatus"))
1194      {
1195        //processStatus(tk);
1196      }
1197      else if (cmd.equalsIgnoreCase("Exit"))
1198      {
1199        myISY.stop();
1200        System.exit(0);
1201      }
1202      else
1203      {
1204        syntaxError();
1205      }
1206    } // try
1207    catch (Exception e)
1208    {
1209      e.printStackTrace();
1210      syntaxError();
1211    }
1212  }
1213
1214}
1215