001/*
002 *  $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/isy/ISYRestRequester.java $
003 *  $Author: tgutwin $
004 *  $Revision: 1091 $
005 *  $Date: 2016-05-29 16:26:54 -0700 (Sun, 29 May 2016) $
006 */
007/*
008 *
009 *  Written by Tom Gutwin - WebARTS Design.
010 *  Copyright (C) 2014-2016 WebARTS Design, North Vancouver Canada
011 *  http://www.webarts.ca
012 *
013 *  This program is free software; you can redistribute it and/or modify
014 *  it under the terms of the GNU General Public License as published by
015 *  the Free Software Foundation; version 3 of the License, or
016 *  (at your option) any later version.
017 *
018 *  This program is distributed in the hope that it will be useful,
019 *  but WITHOUT ANY WARRANTY; without_ even the implied warranty of
020 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
021 *  GNU General Public License for more details.
022 *
023 *  You should have received a copy of the GNU General Public License
024 *  along with this program; if not, write to the Free Software
025 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
026 */
027
028package ca.bc.webarts.tools.isy;
029
030import java.io.IOException;
031import java.lang.Integer;
032import java.net.HttpURLConnection;
033import java.net.MalformedURLException;
034import java.net.URL;
035import java.util.Arrays;
036import java.util.Hashtable;
037import java.util.Set;
038import java.util.Vector;
039
040import ca.bc.webarts.tools.RestRequester;
041import ca.bc.webarts.widgets.Quick;
042
043import org.apache.commons.codec.binary.Base64;
044
045import nu.xom.Attribute;
046import nu.xom.Builder;
047import nu.xom.Document;
048import nu.xom.Element;
049import nu.xom.Elements;
050import nu.xom.Node;
051import nu.xom.ParsingException;
052import nu.xom.ValidityException;
053import nu.xom.Serializer;
054
055
056/**
057  * This class wraps the communication to the REST interface of a
058  * <a href="http://www.universal-devices.com">UDI ISY-994</a>.
059  * It provides many prebuilt java methods that wrap a specific REST call or you can request any one that
060  * is specified in the REST API -
061  * <a href="http://wiki.universal-devices.com/index.php?title=ISY_Developers:API:REST_Interface">
062  * http://wiki.universal-devices.com/index.php?title=ISY_Developers:API:REST_Interface</a>.<br />
063  *  Written by Tom Gutwin - WebARTS Design.<br />
064  *  Copyright &copy; 2014-2016 WebARTS Design, North Vancouver Canada<br />
065  *  <a href="http://www.webarts.ca">http://www.webarts.ca</a>
066  *
067  * @author  Tom B. Gutwin
068  **/
069public class ISYRestRequester extends RestRequester
070{
071  protected static final String CLASSNAME = "ca.bc.webarts.tools.isy.ISYRestRequester"; //ca.bc.webarts.widgets.Util.getCurrentClassName();
072  public static final String LOG_TAG = "\n"+CLASSNAME; //+"."+ca.bc.webarts.android.Util.getCurrentClassName();
073
074  /** DEFAULT ISY994 IP address to use: 10.0.0.207 .**/
075  protected static final String DEFAULT_ISY994_IP = "10.0.0.207";
076  /** DEFAULT ISY994 username to use: admin .**/
077  protected static final String DEFAULT_ISY994_USERNAME = "admin";
078  /** DEFAULT ISY994 password to use: admin .**/
079  protected static final String DEFAULT_ISY994_PASSWORD = "admin";
080  /** DEFAULT ISY994 rest URL to start the URL path: /rest .**/
081  protected static final String DEFAULT_ISY994_REST_URL_PATHSTR = "/rest";
082
083  protected static final String TOMS_ISY994_IP = "10.0.0.207";
084  protected static final String TOMS_ISY994_USERNAME = "admin"; // not really
085  protected static final String TOMS_ISY994_PASSWORD = "admin"; // not really
086
087  protected static StringBuilder helpMsg_ = new StringBuilder(SYSTEM_LINE_SEPERATOR);
088  protected static boolean debugOut_ = false;
089  /** flag to indicate the use of TOMS_isy IP, userID, and password. **/
090  protected static boolean tomsIsy_ = true;
091
092  public static IsyNodes isyNodes_ = null;
093  public static IsyVars isyIntVars_ = null;
094  public static IsyVars isyStateVars_ = null;
095
096  public static String [] ClimateCoverage = {
097  "",
098  "Areas of",
099  "Brief",
100  "Chance of",
101  "Definite",
102  "Frequent",
103  "Intermittent",
104  "Isolated",
105  "Likely",
106  "Numerous",
107  "Occasional",
108  "Patchy",
109  "Periods of",
110  "Slight chance of",
111  "Scattered",
112  "Nearby",
113  "Widespread" };
114
115  public static String [] ClimateIntensity = {
116  "",
117  "Very light",
118  "Light",
119  "Heavy",
120  "Very heavy" };
121
122
123  public static String [] ClimateWeatherConditions = {
124  "",
125  "Hail",
126  "Blowing dust",
127  "Blowing sand",
128  "Mist",
129  "Blowing snow",
130  "Fog",
131  "Frost",
132  "Haze",
133  "Ice Crystals",
134  "Ice fog",
135  "Ice pellets / sleet",
136  "Smoke",
137  "Drizzle",
138  "Rain",
139  "Rain showers",
140  "Rain/snow mix",
141  "Snow/sleet mix",
142  "Wintry mix",
143  "Snow",
144  "Snow showers",
145  "Thunderstorms",
146  "Unknown Precipitation",
147  "Volcanic ash",
148  "Water spouts",
149  "Freezing fog",
150  "Freezing drizzle",
151  "Freezing rain",
152  "Freezing spray" };
153
154  public static String [] ClimateCloudConditions = {
155  "",
156  "Clear        (0-7% of the sky)",
157  "Fair/mostly sunny    (7-32%)",
158  "Partly cloudy        (32-70%)",
159  "Mostly cloudy        (70-95%)",
160  "Cloudy" };
161
162
163  /** The start path to use in therest URL. Over-ride this if you extend this class. **/
164  protected String restUrlPath_ = DEFAULT_ISY994_REST_URL_PATHSTR;
165
166  /**
167    * Default constructor that authenticates the default ISY with the default user password (using the class vars)
168    * UNLESS the tomsIsy_ class var is true to over-ride with TOMS _isy IP, userID, and password.
169    * TOMS settings get 1st priority, and DEFAULTS if {@link  #tomsIsy_ tomsIsy_} class var is false.
170    *
171    * @see #DEFAULT_ISY994_IP
172    * @see #DEFAULT_ISY994_USERNAME
173    * @see #DEFAULT_ISY994_PASSWORD
174    **/
175  public ISYRestRequester()
176  {
177    authenticating_=true;
178    setBaseUrl( "http://"+(tomsIsy_?TOMS_ISY994_IP:DEFAULT_ISY994_IP)+restUrlPath_);
179    setUsername( (tomsIsy_?TOMS_ISY994_USERNAME:DEFAULT_ISY994_USERNAME));
180    setPassword( (tomsIsy_?TOMS_ISY994_PASSWORD:DEFAULT_ISY994_PASSWORD));
181  }
182
183
184  /**
185    * Default constructor that authenticates and connects the ISY with a choice of either the default user password
186    * (using the class vars) or with TOMS _isy IP, userID, and password..
187    *
188    * @see #DEFAULT_ISY994_IP
189    * @see #DEFAULT_ISY994_USERNAME
190    * @see #DEFAULT_ISY994_PASSWORD
191    **/
192  public ISYRestRequester(boolean useDefault)
193  {
194    tomsIsy_=!useDefault;
195    authenticating_=true;
196    setBaseUrl( "http://"+(tomsIsy_?TOMS_ISY994_IP:DEFAULT_ISY994_IP)+restUrlPath_);
197    setUsername( (tomsIsy_?TOMS_ISY994_USERNAME:DEFAULT_ISY994_USERNAME));
198    setPassword( (tomsIsy_?TOMS_ISY994_PASSWORD:DEFAULT_ISY994_PASSWORD));
199  }
200
201
202  /**
203    * Constructor to customize all connection settings.
204    *
205    **/
206  public ISYRestRequester(String server, String user, String pass)
207  {
208    setBaseUrl( "http://"+server+restUrlPath_);
209    authenticating_=true;
210    setUsername(user);
211    setPassword( pass);
212  }
213
214
215  /**
216    * Set Method for class field {@link  #tomsIsy_ tomsIsy_}.
217    *
218    * @param tomsIsy is the value to set this class field to.
219    *
220    **/
221  public static void setTomsIsy_(boolean tomsIsy)
222  {
223    tomsIsy_ = tomsIsy;
224  }  // setTomsIsy Method
225
226
227  /**
228    * Get Method for class field 'tomsIsy_'.
229    *
230    * @return boolean - The value the class field {@link  #tomsIsy_ tomsIsy_}.
231    *
232    **/
233  public static boolean getTomsIsy()
234  {
235    return tomsIsy_;
236  }  // getTomsIsy Method
237
238
239  /**
240    * Set Method for class field 'restUrlPath_'.
241    *
242    * @param restUrlPath_ is the value to set this class field to.
243    *
244    **/
245  public  void setRestUrlPath(String restUrlPath)
246  {
247    restUrlPath_ = restUrlPath;
248  }  // setRestUrlPath Method
249
250
251  /**
252    * Get Method for class field 'restUrlPath_'.
253    *
254    * @return String - The value the class field 'restUrlPath_'.
255    *
256    **/
257  public String getRestUrlPath()
258  {
259    return restUrlPath_;
260  }  // getRestUrlPath Method
261
262
263  /** Check connectivity to the ISY specified by the class parms.
264    * @return true or false
265    **/
266  public boolean canConnect()
267  {
268    if(debugOut_) System.out.println(LOG_TAG+".canConnect("+getBaseUrl()+", "+getUsername()+", "+getPassword()+")");
269
270    boolean retVal = false;
271    if(isInit())
272    {
273      try
274      {
275        if(debugOut_) System.out.println(LOG_TAG+".init = true");
276        String usrlStr = (baseUrl_+"/sys").replace(" " ,"%20");
277        URL url = new URL(usrlStr);
278        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
279        conn.setRequestMethod("GET");
280        if(acceptJSON_)
281          conn.setRequestProperty("Accept", "application/json");
282        else
283          conn.setRequestProperty("Accept", "application/xml");
284
285        if (authenticating_)
286        {
287          //BASE64Encoder enc = new sun.misc.BASE64Encoder();
288          String userpassword = username_ + ":" + password_;
289          //String encodedAuthorization = android.util.Base64.encodeToString( userpassword.getBytes(), android.util.Base64.DEFAULT );
290          String encodedAuthorization = new String(Base64.encodeBase64( (userpassword.getBytes()) ));
291          conn.setRequestProperty("Authorization", "Basic "+ encodedAuthorization);
292        }
293
294        if (conn.getResponseCode() == 200)
295        {
296           retVal=true;
297        } // valid http response code
298        conn.disconnect();
299      }
300      catch (MalformedURLException e)
301      {
302       e.printStackTrace();
303      }
304      catch (IOException e)
305      {
306         e.printStackTrace();
307      }
308    }
309    return retVal;
310  }
311
312
313  /** Sends a REST call to query all nodes and scenes; '/nodes'  . **/
314  public StringBuilder queryAllNodes()
315  {
316    return serviceGet("/nodes");
317  }
318
319
320  /** Sends a REST call to query all scenes; '/nodes/scenes'  . **/
321  public StringBuilder queryAllScenes()
322  {
323    return serviceGet("/nodes/scenes");
324  }
325
326
327  /** Sends a REST call to query all scenes; '/nodes/scenes'  . **/
328  public StringBuilder queryAllNodesAndScenes()
329  {
330    return queryAllNodes().append(queryAllScenes());
331  }
332
333
334  /**
335    * Parses all the NODEs in the ISY into the isyNodes_ var.  If not already parsed, it sends a REST call
336    * to query all nodes abd scenes; '/nodes'  and parses the results into the class object to preapre for use.
337    **/
338  public IsyNodes parseAllNodes(){ return parseAllNodes(false);}
339  /**
340    * Parses all the NODEs in the ISY into the isyNodes_ var.  If not already parsed, it sends a REST call
341    * to query all nodes; '/nodes'  and parses the results into the class object to preapre for use.
342    *
343    * @param reloadFromISY forces the re- query from the isy (else it just returns the existing isyNodes_.
344    **/
345  public IsyNodes parseAllNodes(boolean reloadFromISY)
346  {
347    IsyNodes retVal = isyNodes_;
348    if(reloadFromISY || retVal==null)
349    {
350      String s = "";
351      try
352      {
353        s = queryAllNodes().toString();
354        s.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>","");
355        retVal = new IsyNodes(s);
356        isyNodes_ = retVal;
357      }
358      catch( Exception ex)
359      {
360        // leave it as null
361        ex.printStackTrace();
362
363      }
364    }
365    if(debugOut_) System.out.println("Parsed Nodes:\n"+ isyNodes_);
366    return retVal;
367  }
368
369
370  /** Sends a REST call to query all Variables defined; '/vars/definitions/<varType>'  . **/
371  public StringBuilder queryAllVars(int varType)
372  {
373    parseAllNodes(); // fast return if already parsed
374    return serviceGet("/vars/definitions/"+varType);
375  }
376
377
378  /** Sends a REST call to query all Variables values; 'vars/get/<varType>'  . **/
379  public StringBuilder queryAllVarValues(int varType)
380  {
381    parseAllNodes(); // fast return if already parsed
382    return serviceGet("/vars/get/"+varType);
383  }
384
385
386  /** Fully Queries and loads all Integer vars. **/
387  public StringBuilder queryAllIntegerVars()
388  {
389    parseAllNodes(); // fast return if already parsed
390    StringBuilder retVal = queryAllVars(IsyVars.VAR_TYPE_INT);
391    StringBuilder vVals = queryAllVarValues(IsyVars.VAR_TYPE_INT);
392    try
393    {
394      isyIntVars_ = new IsyVars(IsyVars.VAR_TYPE_INT, retVal, vVals) ;
395    }
396    catch (Exception ex)
397    {
398      retVal = null;
399      isyIntVars_ = null;
400      System.out.println("ERROR  querying Integer Vars");
401      ex.printStackTrace();
402    }
403    return retVal;
404  }
405
406
407  /** Fully Queries and loads all state vars. **/
408  public StringBuilder queryAllStateVars()
409  {
410    parseAllNodes(); // fast return if already parsed
411    StringBuilder retVal = queryAllVars(IsyVars.VAR_TYPE_STATE);
412    StringBuilder vVals = queryAllVarValues(IsyVars.VAR_TYPE_STATE);
413    try
414    {
415      isyStateVars_ = new IsyVars(IsyVars.VAR_TYPE_STATE, retVal, vVals) ;
416    }
417    catch (Exception ex)
418    {
419      retVal = null;
420      isyStateVars_ = null;
421      System.out.println("ERROR  querying state Vars");
422      ex.printStackTrace();
423    }
424    return retVal;
425  }
426
427
428  /** Returns class var 'isyIntVars_'.
429    *
430    * @return the class IsyVars var holding Integers.
431    **/
432  public IsyVars getIsyIntVars()
433  {
434    parseAllNodes(); // fast return if already parsed
435    if(isyIntVars_==null) queryAllIntegerVars();
436    return isyIntVars_;
437  }
438
439
440  /** Returns class var 'isyStateVars_'.
441    *
442    * @return the class IsyVars var holding the State Vars.
443    **/
444  public IsyVars getIsyStateVars()
445  {
446    parseAllNodes(); // fast return if already parsed
447    if(isyStateVars_==null)queryAllStateVars();
448    return isyStateVars_;
449  }
450
451
452  /** Not Implemented. **/
453  public StringBuilder queryAllIntVarVals()
454  {
455    parseAllNodes(); // fast return if already parsed
456    StringBuilder retVal = null;
457    return retVal;
458  }
459
460
461  /**
462    * Sends a REST call turn a Node or scene ON '/nodes/<addressOfNodeName>/cmd/DON' .
463    *
464    * @param nodeName the nodes NAME to turn ON
465    **/
466  public StringBuilder deviceOn(String nodeName)
467  {
468    parseAllNodes(); // fast return if already parsed
469    String addr = isyNodes_.getNodeAddress(nodeName);
470    if(debugOut_) System.out.println(LOG_TAG+".deviceOn "+nodeName+" "+"("+addr+")");
471    return deviceAddressOn(addr);
472  }
473
474
475  /**
476    * Sends a REST call turn a Node or scene OFF '/nodes/<addressOfNodeName>/cmd/DOF' .
477    *
478    * @param nodeName the nodes NAME to turn OFF
479    **/
480  public StringBuilder deviceOff(String nodeName)
481  {
482    parseAllNodes(); // fast return if already parsed
483    String addr = isyNodes_.getNodeAddress(nodeName);
484    if(debugOut_) System.out.println(LOG_TAG+".deviceOff "+nodeName+" "+"("+addr+")");
485    return deviceAddressOff(addr);
486  }
487
488
489  /**
490    * Sends a REST call turn a Node or scene ON '/nodes/<addressOfNodeName>/cmd/DON' .
491    *
492    * @param addr the nodes address (ie. 16 10 A0 1) to turn OFF
493    **/
494  public StringBuilder deviceAddressOn(String addr)
495  {
496    parseAllNodes(); // fast return if already parsed
497    return serviceGet("/nodes/"+addr+"/cmd/DON");
498  }
499
500
501  /**
502    * Sends a REST call turn a Node ON '/nodes/<addressOfNodeName>/cmd/DON' .
503    *
504    * @param addr the nodes address (ie. 16 10 A0 1) to turn On
505    * @param brightness the brightness (0-255)
506    **/
507  public StringBuilder deviceAddressOn(String addr, int brightness)
508  {
509    parseAllNodes(); // fast return if already parsed
510    if(brightness<0) brightness=0;
511    else if(brightness>255) brightness=255;
512
513    return serviceGet("/nodes/"+addr+"/cmd/DON/"+brightness);
514  }
515
516
517  /**
518    * Sends a REST call turn a Node OFF '/nodes/<addressOfNodeName>/cmd/DOF' .
519    *
520    * @param addr the nodes address (ie. 16 10 A0 1) to turn OFF
521    **/
522  public StringBuilder deviceAddressOff(String addr)
523  {
524    parseAllNodes(); // fast return if already parsed
525    return serviceGet("/nodes/"+addr+"/cmd/DOF");
526  }
527
528
529  /**
530    * Toggles the state of the device/node NAMED nodeName.
531    *
532    * @param nodeName the nodes NAME to toggle its state
533    **/
534  public int deviceToggle(String nodeName)
535  {
536    parseAllNodes(); // fast return if already parsed
537    String addr = isyNodes_.getNodeAddress(nodeName);
538    if(debugOut_) System.out.println(LOG_TAG+".deviceToggle("+nodeName+")");
539    //ca.bc.webarts.widgets.Util.sleep(400);
540    return deviceAddressToggle(addr);
541  }
542
543
544  /** Toggles the ON/OFF state specified device. **/
545  public int deviceAddressToggle(String addr)
546  {
547    parseAllNodes(); // fast return if already parsed
548    int retVal = -1;
549    StringBuilder restResponse = null;
550    int offLevel = 5;
551    if(debugOut_) System.out.println(LOG_TAG+".deviceAddressToggle("+addr+")");
552    int stat = deviceAddressStatus(addr);
553    if(debugOut_) System.out.println(LOG_TAG+".deviceAddressToggle stat="+stat);
554    if (stat<offLevel) restResponse = deviceAddressOn(addr);
555    else restResponse = deviceAddressOff(addr);
556
557    boolean success = false;
558    if(restResponse!=null && restResponse.toString().indexOf("succeeded=\"true\"><status>200</status>")!=-1) success = true;
559    if (success && stat<offLevel) retVal = 100;
560    else if (success) retVal = 0;
561
562    // now to make sure the on level is correct
563    if(retVal!=-1) retVal = deviceAddressStatus(addr);
564    return retVal;
565  }
566
567
568  /**
569    * Queries the status of the device/node NAMED nodeName.
570    *
571    * @param nodeName the nodes NAME to query its status
572    **/
573  public int deviceStatus(String nodeName)
574  {
575    parseAllNodes(); // fast return if already parsed
576    int retVal = -1;
577    String addr = isyNodes_.getNodeAddress(nodeName);
578    return deviceAddressStatus(addr);
579  }
580
581
582  public int deviceAddressStatus(String addr){ return deviceAddressStatus(addr, true);}
583  /** returns the on/off/% for a specified node address.
584    * @return on=100, off=0, or any number in between 0-100
585    **/
586  public int deviceAddressStatus(String addr, boolean includeScenes)
587  {
588    parseAllNodes(); // fast return if already parsed
589    int retVal = -1;
590    StringBuilder resp =  serviceGet("/status/"+addr);
591    /* The response holds and attribute called formatted that can be one of Off, On , %   */
592    /*  <property id="ST" value="114" formatted="45" uom="%/on/off"/>   */
593    //System.out.println("  DEBUG1   respStr: "+resp.toString());
594    if (resp!=null)
595    {
596      String respStr = resp.toString();
597      int fSpot = respStr.indexOf("formatted");
598      String fVal = respStr.substring(fSpot+11,respStr.indexOf("\"", fSpot+12));
599      if(debugOut_) System.out.println("STATUS: "+ addr+": "+fVal+"\n"+resp.toString());
600      if(fVal.equalsIgnoreCase("Off") ) retVal = 0;
601      else if (fVal.equalsIgnoreCase("On") ) retVal = 100;
602      else if (fVal.trim().equals("") ) retVal = -1;
603      else if (fVal.trim().indexOf(".")!=-1 )
604        try{retVal = Integer.parseInt(fVal.substring(0,fVal.indexOf(".")));}
605        catch(Exception ex){System.out.println("ERROR pulling the status value from "+fVal+" "+fVal.substring(0,fVal.indexOf(".")));}
606      else
607        try{retVal = Integer.parseInt(fVal);}
608        catch(Exception ex){System.out.println("ERROR pulling the int status value from "+fVal);}
609    }
610    else if(includeScenes) // try it as a scene/group
611      retVal = groupAddressStatus(addr);
612
613    return retVal;
614  }
615
616
617  /** returns the on/off/% fora specified scene/group address.
618    * @return on=100, off=0, or any number in between 0-100
619    **/
620  public int groupAddressStatus(String addr)
621  {
622    parseAllNodes(); // fast return if already parsed
623    int retVal = -1;
624    String respStr = "";
625    String groupMembersXml = "";
626    String addr2 = "";
627    int fSpot = -1;
628    String fVal = "";
629
630    //StringBuilder resp =  serviceGet("/nodes/"+addr+"?members=true");
631    /* The response holds the group members that can be one of Off, On , %   */
632    /* <nodeInfo>
633         <group flag="132">
634           <address>36083</address>
635           <name>UpUnderCabLights</name>
636           <parent type="3">29673</parent>
637           <deviceGroup>29</deviceGroup>
638           <pnode>36083</pnode>
639           <ELK_ID>F01</ELK_ID>
640           <members>
641             <link type="16">17 54 1 1</link>
642             <link type="16">43 A2 FB 5</link>
643           </members>
644         </group>
645       </nodeInfo> */
646    // the isyNodes_ now has a method called getGroupMembers(sceneName) that can be used to check status
647    String groupName = isyNodes_.getGroupName(addr);
648    GroupMembers gMems = isyNodes_.getGroupMembers(groupName);
649    String [] members = gMems.getMemberNodes();
650    int memStats = 0;
651    for(String memAddr : members)
652    {
653      memStats+=deviceAddressStatus(memAddr,false); // this could be recursive if not sent a false
654    }
655    if(members.length>0)
656      retVal = memStats/members.length;
657    else
658      retVal = memStats;
659
660    /*if (resp!=null)
661    {
662      respStr = resp.toString();
663      System.out.println("  DEBUG2   respStr: "+respStr);
664      groupMembersXml = respStr.substring(respStr.indexOf("<link type="), respStr.indexOf("</members>"));
665      System.out.println("  DEBUG3   groupMembersXml: "+groupMembersXml);
666      addr2 = groupMembersXml.substring(groupMembersXml.indexOf(">")+1, groupMembersXml.indexOf("<"));
667      System.out.println("  DEBUG4   addr: "+addr2);
668      retVal = deviceAddressStatus(addr2);
669      if(debugOut_) System.out.println("STATUS: "+ addr+": "+retVal+"\n");
670    }
671    */
672
673    return retVal;
674  }
675
676
677  /** Sets a ISY variable.
678    * @param varName this will lookup the varID
679    * @param varVal
680    * @param varType send a 1 for Integer and 2 for State
681  **/
682  /*
683  public StringBuilder setVariable(String varName, String varVal, int varType)
684  {
685    parseAllNodes(); // fast return if already parsed
686    return setVariable(getVarId(varName), varVal, varType);
687  }
688  */
689
690  /** Sets a ISY variable.
691    * @param varName
692    * @param varId
693    * @param varType send a 1 for Integer and 2 for State
694  **/
695  public StringBuilder setVariable(int varId, String varVal, int varType)
696  {
697    StringBuilder sb = null;
698    if (varId>0 && varVal!=null && (varType==1 || varType==2))
699      sb = serviceGet("/vars/set/"+varType+"/"+varId+"/"+varVal);
700    return sb;
701  }
702
703
704  /** Gets the current XML (StringBuffer) result for an ISY variable.
705    * @param varID
706    * @param varType send a 1 for Integer and 2 for State
707    * @return the XML returned from the ISY for the var requested , OR null if incorrect varType specified.
708  **/
709  public StringBuilder getVariable(String varID, int varType)
710  {
711    StringBuilder sb = null;
712    if (varID!=null && !varID.equals("") &&
713        (varType==1 || varType==2))
714      sb = serviceGet("/vars/get/"+varType+"/"+varID);
715    return sb;
716  }
717
718
719  /** Gets the current XML (StringBuffer) result for an ISY variable.
720    * @param varID
721    * @param varType send a 1 for Integer and 2 for State
722    * @return int current value
723  **/
724  public int getVariableIntValue(String varID, int varType) throws Exception
725  {
726    int retVal = 0;
727    StringBuilder sb = null;
728    if (varID!=null && !varID.equals("") &&
729        (varType==1 || varType==2))
730      sb = serviceGet("/vars/get/"+varType+"/"+varID);
731      int valSpot = sb.toString().indexOf("<val>");
732      if(valSpot!=-1)
733      {
734        String valStr = sb.toString().substring(valSpot+"<val>".length(),sb.toString().indexOf("</val>"));
735        retVal = Integer.parseInt(valStr);
736      }
737      else throw new Exception((varType == 1?"Integer ":"State ")+"VarID: "+varID +" NOT found.");
738
739    return retVal;
740  }
741
742
743  /** Gets a the next un=used/avaialbe varID.
744    * if already parsed, this method will NOT re-query the vars unless told to.
745    * @return an int for the next ID to use
746  **/
747  public int getNextAvailableStateVarID(){ return getNextAvailableStateVarID(false);}
748  public int getNextAvailableStateVarID(boolean reQueryVars)
749  {
750    int retVal = 0;
751    if(isyStateVars_==null || reQueryVars)
752    {
753      queryAllStateVars();
754    }
755    if(isyStateVars_!=null)
756    {
757      retVal = isyStateVars_.getNextUnusedID();
758    }
759    return retVal;
760  }
761
762
763  /** Gets a the next un=used/avaialbe varID.
764    * if already parsed, this method will NOT re-query the vars unless told to.
765    * @return an int for the next ID to use
766  **/
767  public int getNextAvailableIntVarID(){ return getNextAvailableIntVarID(false);}
768  public int getNextAvailableIntVarID(boolean reQueryVars)
769  {
770    int retVal = 0;
771    if(isyIntVars_==null || reQueryVars)
772    {
773      queryAllIntegerVars();
774    }
775    if(isyIntVars_!=null)
776    {
777      retVal = isyIntVars_.getNextUnusedID();
778    }
779    return retVal;
780  }
781
782
783  /** returns the status for all the nodes. **/
784  public StringBuilder getStatus()
785  {
786    return serviceGet("/status");
787  }
788
789
790  /** queries all the nodes. **/
791  public StringBuilder getQuery()
792  {
793    return serviceGet("/query");
794  }
795
796
797  /** queries the nodes with name.**/
798  public StringBuilder getQueryNode(String nodeName)
799  {
800    String nAddr = isyNodes_.getNodeAddress(nodeName);
801    return serviceGet("/query/"+nAddr);
802  }
803
804
805  /** queries the nodes with address.**/
806  public StringBuilder getQueryNodeFromAddress(String nAddr)
807  {
808    return serviceGet("/query/"+nAddr);
809  }
810
811
812  /** returns all the nodes. **/
813  public StringBuilder getNodes()
814  {
815    return serviceGet("/nodes");
816  }
817
818
819  /** returns isy config. **/
820  public StringBuilder getConfig()
821  {
822    return serviceGet("/config");
823  }
824
825
826  /** returns a devices property Value (which is the unformatted value) String with /rest/nodes/<node>/<property> .
827    *
828    * @return the property value as an string
829    **/
830  public String devicePropertyValue(String nodeName, String prop)
831  {
832    parseAllNodes(); // fast return if already parsed
833    String addr = isyNodes_.getNodeAddress(nodeName);
834    IsyDeviceProperty devProp = deviceAddressProperty(addr, prop);
835    return devProp.getValue();
836  }
837
838
839  /** returns a devices property formatted String with /rest/nodes/<node>/<property> .
840    *
841    * @return the property value as a String
842    **/
843  public String devicePropertyFormattedValue(String nodeName, String prop)
844  {
845    parseAllNodes(); // fast return if already parsed
846    String addr = isyNodes_.getNodeAddress(nodeName);
847    IsyDeviceProperty devProp = deviceAddressProperty(addr, prop);
848    return devProp.getFormatted();
849  }
850
851
852  /** returns a devices property XML String with /rest/nodes/<node>/<property> .
853    *
854    * @return the property XML that gets returned from the rest call
855    **/
856  public String devicePropertyXML(String nodeName, String prop)
857  {
858    parseAllNodes(); // fast return if already parsed
859    String addr = isyNodes_.getNodeAddress(nodeName);
860    return deviceAddressPropertyXML(addr, prop);
861  }
862
863
864  /** returns a devices property XML String with /rest/nodes/<nodeID>/<property> .
865    *
866    * @return the property XML that gets returned from the rest call
867    **/
868  public String deviceAddressPropertyXML(String addr, String prop)
869  {
870    IsyDeviceProperty devProp = deviceAddressProperty(addr, prop);
871    return devProp.getElementStr();
872  }
873
874
875  /** returns a devices property int value with /rest/nodes/<nodeID>/<property> .
876    *
877    * @return the property value as an int, or -1 if it is not an Integer type
878    **/
879  public int deviceAddressIntProperty(String addr, String prop)
880  {
881    IsyDeviceProperty devProp = deviceAddressProperty(addr, prop);
882    return devProp.getIntValue();
883  }
884
885
886  /** returns a devices property int value with /rest/nodes/<nodeID>/<property> .
887    *
888    * @return the property value unit of measure (uom)
889    **/
890  public String deviceAddressPropertyUOM(String addr, String prop)
891  {
892    IsyDeviceProperty devProp = deviceAddressProperty(addr, prop);
893    return devProp.getUom();
894  }
895
896
897  /** returns a devices property formatted String with /rest/nodes/<node>/<property> .
898    *
899    * @return the property formatted value as a String
900    **/
901  public IsyDeviceProperty deviceProperty(String nodeName, String prop)
902  {
903    parseAllNodes(); // fast return if already parsed
904    String addr = isyNodes_.getNodeAddress(nodeName);
905    return deviceAddressProperty(addr, prop);
906  }
907
908
909  /** returns a devices property with /rest/nodes/<nodeID>/<property> . **/
910  public IsyDeviceProperty deviceAddressProperty(String addr, String prop)
911  {
912    int retVal = -1;
913    String requestStr = "/nodes/" + addr +"/"+prop;
914    if (debugOut_) System.out.println("Rest Reguest= "+getServiceUrl(requestStr));
915    getQueryNodeFromAddress(addr); // this makkes sure the node status is up to date
916    StringBuilder resp = serviceGet(requestStr);
917    IsyDeviceProperty devProp = new IsyDeviceProperty(resp.toString());
918    return devProp;
919  }
920
921
922  /**
923   * Class main commandLine entry method that has a test command and some convienience commands, as well as a pure rest command.
924   **/
925  public static void main(String [] args)
926  {
927    final String methodName = CLASSNAME + ": main()";
928    ISYRestRequester instance = new ISYRestRequester();
929    IsyNodes isyNodes = instance.parseAllNodes(true);
930
931    /* Simple way af parsing the args */
932    if (args ==null || args.length<1)
933      System.out.println(getHelpMsgStr());
934    /* *************************************** */
935    else
936    {
937      if (args[0].equalsIgnoreCase("test"))
938      {
939        instance.testCMD(args);
940      }
941      /* *************************************** */
942      else if (args[0].equalsIgnoreCase("listNodes") || args[0].equalsIgnoreCase("nodes"))
943      {
944        instance.listNodesCMD(args);
945      }
946      /* *************************************** */
947      else if (args[0].equalsIgnoreCase("listScenesAndNodes"))
948      {
949        instance.listScenesCMD(args);
950        instance.listNodesCMD(args);
951      }
952      /* *************************************** */
953      else if (args[0].equalsIgnoreCase("listScenes") || args[0].equalsIgnoreCase("scenes"))
954      {
955        instance.listScenesCMD(args);
956      }
957      /* *************************************** */
958      else if (args[0].equalsIgnoreCase("getNodeAddress") && args.length>1)
959      {
960        try { System.out.println(args[1]+" : " +isyNodes.getNodeAddress(args[1]));}
961        catch(Exception ex){ System.out.println("Error retrieving Node Address for : "+args[1]+"\n"+Arrays.toString(args));}
962      }
963      /* *************************************** */
964      else if (args[0].equalsIgnoreCase("listVars"))
965      {
966        instance.listVarsCMD(args);
967      }
968      /* *************************************** */
969      else if (args[0].equalsIgnoreCase("getVar"))
970      {
971        try { instance.getVarCMD(args);}
972        catch(Exception ex){ System.out.println("Error retrieving VAR: "+Arrays.toString(args));}
973      }
974      /* *************************************** */
975      else if (args[0].equalsIgnoreCase("getProperty"))
976      {
977        instance.getPropertyCMD(args);
978      }
979      /* *************************************** */
980      else if (args[0].equalsIgnoreCase("toggle"))
981      {
982        instance.toggleCMD(args);
983      }
984      /* *************************************** */
985      else if (args[0].equalsIgnoreCase("status"))
986      {
987        instance.statusCMD(args);
988      }
989      /* *************************************** */
990      else
991      {
992        instance.restCMD(args);
993      }
994    }
995  } // main
996
997
998  /**
999   * commandLine command executor method for the test Command.
1000   * @param args the array of commandLine args that got passed in
1001   **/
1002  protected void testCMD(String [] args)
1003  {
1004    final String methodName = CLASSNAME + ": testCMD(String [])";
1005    parseAllNodes(); // fast return if already parsed
1006
1007
1008    System.out.println("Testing ISY Rest Service: "+ "/sys");
1009    StringBuilder resp =  serviceGet("/sys");
1010    System.out.println(resp.toString()); System.out.println();
1011    System.out.println("Testing ISY Rest Service: "+ "Turn the Downstairs Kitchen Lights on: "+ isyNodes_.getNodeName(DWNKITCHEN_ADDR) +" ("+DWNKITCHEN_ADDR+")");
1012    resp = deviceAddressOn(DWNKITCHEN_ADDR);
1013    System.out.println(resp.toString());  System.out.println();
1014    //ca.bc.webarts.widgets.Util.sleep(400);
1015    System.out.println("Testing ISY Rest Service: "+ "checking status of the "+GAMESROOMLIGHTS_NODENAME);
1016    int devStatus = deviceStatus(GAMESROOMLIGHTS_NODENAME);
1017    System.out.println("GAMESROOMLIGHTS level:"+devStatus); System.out.println();
1018
1019  }
1020
1021
1022  /**
1023   * commandLine command executor method for the listNodes Command.
1024   * @param args the array of commandLine args that got passed in
1025   **/
1026  protected void listNodesCMD(String [] args)
1027  {
1028    final String methodName = CLASSNAME + ": listNodesCMD(String [])";
1029    if (debugOut_) System.out.println("ISY Rest Services: "+ "listNodes");
1030    parseAllNodes(); // fast return if already parsed
1031    if (isyNodes_!=null)
1032    {
1033      String [] resp =  isyNodes_.getNodeNamesCopy(true); // true means sorted
1034      String [] resp2 =  isyNodes_.getGroupNamesCopy(true); // true means sorted
1035      if(false)
1036      {
1037        if (debugOut_) System.out.println("---- BEFORE SORT:");
1038        for(int i=0; i< resp.length;i++)
1039          System.out.println(resp[i]+" ("+isyNodes_.getNodeTypeStr(resp[i])+"): "+isyNodes_.getNodeAddress(resp[i]));
1040        if (debugOut_) System.out.println("\n*******\n---- AFTER SORT:");
1041      }
1042      //Quick.sort(resp);
1043      if (resp!=null)
1044        for(int i=0; i< resp.length;i++)
1045          System.out.println(resp[i]+" ("+isyNodes_.getNodeTypeStr(resp[i])+"): "+isyNodes_.getNodeAddress(resp[i]));
1046      if (resp2!=null)
1047        for(int i=0; i< resp2.length;i++)
1048          System.out.println(resp2[i]+" : "+isyNodes_.getGroupAddress(resp2[i]));
1049    }
1050    else
1051      System.out.println("   ERROR Parsing isyNodes.");
1052
1053  }
1054
1055
1056  /**
1057   * commandLine command executor method for the listScenes Command.
1058   * @param args the array of commandLine args that got passed in
1059   **/
1060  protected void listScenesCMD(String [] args)
1061  {
1062    final String methodName = CLASSNAME + ": listScenesCMD(String [])";
1063    if (debugOut_) System.out.println("ISY Rest Services: "+ "listScenes");
1064    parseAllNodes(); // fast return if already parsed
1065    if (isyNodes_!=null)
1066    {
1067      String [] resp =  isyNodes_.getGroupNamesCopy(true); // true means sorted
1068      //Quick.sort(resp);
1069      if (resp!=null)
1070        for(int i=0; i< resp.length;i++)
1071          System.out.println(resp[i]+": "+isyNodes_.getSceneAddress(resp[i]));
1072    }
1073    else
1074      System.out.println("   ERROR Parsing isyNodes.");
1075
1076  }
1077
1078
1079  /**
1080   * commandLine command executor method for the listVars Command.
1081   * @param args the array of commandLine args that got passed in
1082   **/
1083  protected void listVarsCMD(String [] args)
1084  {
1085    final String methodName = CLASSNAME + ": listVarsCMD(String [])";
1086    if (debugOut_) System.out.println("ISY Rest Services: "+ "listVars");
1087    queryAllIntegerVars();
1088    queryAllStateVars();
1089    //System.out.println(instance.isyIntVars_.toString());
1090    //System.out.println(instance.isyStateVars_.toString());
1091
1092    if (isyIntVars_!=null)
1093    {
1094      System.out.println("\n"+isyIntVars_.getNumVars()+" INTEGER Vars");
1095      System.out.println("------------");
1096      String [] resp =  isyIntVars_.getVarNames();
1097
1098      if (resp!=null)
1099      {
1100        Quick.sort(resp);
1101        for(int i=0; i< resp.length;i++)
1102        {
1103          try{ System.out.println(resp[i]+": "+isyIntVars_.getVarValue(resp[i]));}
1104          catch(Exception ex){System.out.println(resp[i]+": "+"NOT FOUND");}
1105        }
1106      }
1107    }
1108    else
1109      System.out.println("   ERROR Parsing Integer Vars.");
1110
1111    if (isyStateVars_!=null)
1112    {
1113      System.out.println("\n"+isyStateVars_.getNumVars()+" STATE Vars");
1114      System.out.println("------------");
1115      String [] resp =  isyStateVars_.getVarNames();
1116      if (resp!=null)
1117      {
1118        Quick.sort(resp);
1119        for(int i=0; i< resp.length;i++)
1120        {
1121          try{ System.out.println(resp[i]+": "+isyStateVars_.getVarValue(resp[i]));}
1122          catch(Exception ex){System.out.println(resp[i]+": "+"NOT FOUND");}
1123        }
1124      }
1125    }
1126    else
1127      System.out.println("   ERROR Parsing State Vars.");
1128
1129  }
1130
1131
1132  /**
1133   * commandLine command executor method for the toggle Command.
1134   * @param args the lightname to toggle
1135   **/
1136  public int toggleLight(String  arg)
1137  {
1138     String [] args = {"toggle", arg};
1139     return toggleCMD(args);
1140  }
1141
1142
1143  /**
1144   * commandLine command executor method for the toggle Command.
1145   * @param args the array of commandLine args that got passed in
1146   **/
1147  protected int toggleCMD(String [] args)
1148  {
1149    final String methodName = CLASSNAME + ": toggleCMD(String [])";
1150    int devStatus = -1;
1151    if (args.length>1 && args[0].equals("toggle"))  parseAllNodes();
1152    if (isyNodes_!=null && args.length>1)
1153    {
1154      StringBuilder nName = new StringBuilder(args[1].trim());
1155      if (args.length>2) for (int i=2;i<args.length; i++) {nName.append(" ");nName.append(args[i]);}
1156      if ( isyNodes_.hasNodeName(nName.toString()) || isyNodes_.hasGroupName(nName.toString()))
1157      {
1158        String nAddr = isyNodes_.getNodeAddress( nName.toString());
1159        System.out.println("Toggling: "+ nName.toString()+" ( "+ nAddr+" )");
1160        devStatus = deviceAddressToggle(nAddr);
1161        //devStatus = deviceAddressStatus(nAddr);
1162        System.out.println(nName.toString().trim()+" level:"+devStatus);
1163
1164      }
1165      else
1166      System.out.println("   ERROR can't find '"+args[1].trim()+"' in isyNodes");
1167    }
1168    else
1169      System.out.println("   ERROR Parsing isyNodes.");
1170
1171    return devStatus;
1172  }
1173
1174
1175  /**
1176   * commandLine status command method for the status Command.
1177   * @param args the array of commandLine args that got passed in
1178   *
1179   * @param args is an array of stringas that get concatenated into the single NODE name to get status for.
1180   **/
1181  protected void statusCMD(String [] args)
1182  {
1183    final String methodName = CLASSNAME + ": statusCMD(String [])";
1184    parseAllNodes(); // fast return if already parsed
1185    if (debugOut_) System.out.println("ISY Rest Services: "+ "node status");
1186    if (args.length>1 && args[0].equals("status")) parseAllNodes();
1187    if (isyNodes_!=null && args.length>1)
1188    {
1189      StringBuilder nName = new StringBuilder(args[1]);
1190      if (args.length>2) for (int i=2;i<args.length; i++) {nName.append(" ");nName.append(args[i]);}
1191      if ( isyNodes_.hasNodeName(nName.toString()) || isyNodes_.hasGroupName(nName.toString()))
1192      {
1193        String nAddr = isyNodes_.getNodeAddress( nName.toString());
1194        //System.out.println(" for node: "+nAddr.toString()+" ( "+ isyNodes.getNodeName( nAddr.toString()+" )"));
1195        int devStatus = deviceAddressStatus(nAddr);
1196        System.out.println("  "+nName.toString()+" ( "+ nAddr+" ) status is "+devStatus);
1197      }
1198      else
1199        System.out.println("   ERROR Can't find that node NAME = "+ nName);
1200    }
1201    else
1202      System.out.println("   ERROR Parsing node NAME ");
1203  }
1204
1205
1206  /**
1207   * commandLine command executor method for the default rest Command.
1208   * It treats each arg as a part of a single rest command and passes it along to the ISY.
1209   * @param args the array of commandLine args that got passed in
1210   **/
1211  protected void restCMD(String [] args)
1212  {
1213    final String methodName = CLASSNAME + ": restCMD(String [])";
1214    // Parse the command
1215    String allcommands = args[0];
1216    for (int i=1;i< args.length;i++) allcommands+=" "+args[i];
1217    if (debugOut_) System.out.print("Sending ISY Rest Service: "+allcommands);
1218    String passedCommand = (allcommands.startsWith(restUrlPath_+"/")?allcommands.substring(restUrlPath_.length()):allcommands);
1219    System.out.println(" ("+passedCommand+")");
1220    passedCommand = (passedCommand.startsWith("/")?passedCommand:"/"+passedCommand);
1221    StringBuilder resp =  serviceGet(passedCommand);
1222    if (resp!=null)
1223    {
1224      System.out.println(responseIndenter(resp).toString());
1225      System.out.println();
1226    }
1227    else
1228    {
1229      System.out.println("Response Error");
1230      System.out.println();
1231    }
1232
1233  }
1234
1235
1236  protected int getVarCMD(String arg) throws Exception
1237  {
1238    String [] args = {arg};
1239    return getVarCMD(args);
1240  }
1241
1242
1243  /**
1244   * commandLine command executor method for the getVar Command.
1245   * @param args the array of commandLine args that got passed in
1246   **/
1247  protected int getVarCMD(String [] args) throws Exception
1248  {
1249    final String methodName = CLASSNAME + ": getVarCMD(String [])";
1250    parseAllNodes(); // fast return if already parsed
1251    int retVal = -999;
1252    if (debugOut_) System.out.println("ISY Rest Services: "+ "getVar");
1253    queryAllIntegerVars();
1254    queryAllStateVars();
1255
1256    retVal = isyIntVars_.getVarValue(args[0]);
1257    System.out.println(args[0]+" = "+retVal);
1258    return retVal;
1259  }
1260
1261
1262  /**
1263   * commandLine command executor method for the getProperty Command.
1264   * @param args the array of commandLine args that got passed in
1265   **/
1266  protected String getPropertyValue(String nodeName, String propName)
1267  {
1268    final String methodName = CLASSNAME + ": getPropertyValue(String [])";
1269    String retVal = "";
1270
1271    if(debugOut_) System.out.println("ISY Rest Services: "+ "node property");
1272    parseAllNodes();
1273    if (isyNodes_!=null && nodeName!=null && propName!=null
1274        && !"".equals(nodeName) && !"".equals(propName))
1275    {
1276      String nName = nodeName;
1277      String pName = propName;
1278
1279      if(debugOut_) System.out.println("Looking for property '"+pName+"' on nodeName '"+nName+"'");
1280      IsyDeviceProperty isyProp = deviceProperty(nName, pName);
1281      String propElementStr = isyProp.getElementStr();
1282      if(debugOut_) System.out.println(nName);
1283      if(debugOut_) System.out.println(propElementStr);
1284      if(debugOut_) System.out.println("             Value="+isyProp.getValue());
1285      if(debugOut_) System.out.println("   Formatted Value="+isyProp.getFormatted()+" "+isyProp.getUom());
1286      if(debugOut_) System.out.println("     Integer Value="+isyProp.getIntValue());
1287      retVal = isyProp.getValue();
1288    }
1289    else
1290      System.out.println("ERROR on comnmandLine: getProperty command requires a nodeName and a propertyName");
1291
1292    return retVal;
1293  }
1294
1295
1296  /**
1297   * commandLine command executor method for the getProperty Command.
1298   * @param args the array of commandLine args that got passed in
1299   **/
1300  protected String getPropertyCMD(String [] args)
1301  {
1302    final String methodName = CLASSNAME + ": getPropertyCMD(String [])";
1303    String retVal = "";
1304
1305    if(debugOut_) System.out.println("ISY Rest Services: "+ "node property");
1306    if (args.length>2 && args[0].equalsIgnoreCase("getProperty")) parseAllNodes();
1307    if (isyNodes_!=null && args.length>2)
1308    {
1309      String nName = args[1];
1310      for (int i=2;i< args.length-1;i++) nName+=" "+args[i];
1311      String pName = args[args.length-1];
1312
1313      if(debugOut_) System.out.println("Looking for property '"+pName+"' on nodeName '"+nName+"'");
1314      IsyDeviceProperty isyProp = deviceProperty(nName, pName);
1315      String propElementStr = isyProp.getElementStr();
1316      System.out.println(nName);
1317      System.out.println(propElementStr);
1318      System.out.println("             Value="+isyProp.getValue());
1319      System.out.println("   Formatted Value="+isyProp.getFormatted()+" "+isyProp.getUom());
1320      System.out.println("     Integer Value="+isyProp.getIntValue());
1321      retVal = isyProp.getValue();
1322    }
1323    else
1324      System.out.println("ERROR on comnmandLine: getProperty command requires a nodeName and a propertyName");
1325
1326    return retVal;
1327  }
1328
1329
1330  /**
1331   * commandLine command executor method for the getProperty Command.
1332   * @param args the array of commandLine args that got passed in
1333   **/
1334  protected String getPropertyUOM(String [] args)
1335  {
1336    final String methodName = CLASSNAME + ": getPropertyCMD(String [])";
1337    String retVal = "";
1338
1339    if(debugOut_) System.out.println("ISY Rest Services: "+ "node property");
1340    if (args.length>2 && args[0].equalsIgnoreCase("getProperty")) parseAllNodes();
1341    if (isyNodes_!=null && args.length>2)
1342    {
1343      String nName = args[1];
1344      for (int i=2;i< args.length-1;i++) nName+=" "+args[i];
1345      String pName = args[args.length-1];
1346
1347      if(debugOut_) System.out.println("Looking for property '"+pName+"' on nodeName '"+nName+"'");
1348      IsyDeviceProperty isyProp = deviceProperty(nName, pName);
1349      String propElementStr = isyProp.getElementStr();
1350      System.out.println(nName);
1351      System.out.println(propElementStr);
1352      System.out.println("             Value="+isyProp.getValue());
1353      System.out.println("   Formatted Value="+isyProp.getFormatted()+" "+isyProp.getUom());
1354      System.out.println("     Integer Value="+isyProp.getIntValue());
1355      retVal = isyProp.getUom();
1356    }
1357    else
1358      System.out.println("ERROR on comnmandLine: getProperty command requires a nodeName and a propertyName");
1359
1360    return retVal;
1361  }
1362
1363
1364  /**
1365   * Template method for future commandLine command executor methods.
1366   * @param args the array of commandLine args that got passed in
1367   **/
1368  protected void templateCMD(String [] args)
1369  {
1370    final String methodName = CLASSNAME + ": testCMD(String [])";
1371
1372  }
1373
1374
1375    /** gets the help as a String.
1376   * @return the helpMsg in String form
1377   **/
1378  protected static String getHelpMsgStr() {return getHelpMsg().toString();}
1379
1380
1381  /** initializes and gets the helpMsg_
1382  class var.
1383   * @return the class var helpMsg_
1384   **/
1385  protected static StringBuilder getHelpMsg()
1386  {
1387    helpMsg_ = new StringBuilder(SYSTEM_LINE_SEPERATOR);
1388    helpMsg_.append("---  WebARTS "+CLASSNAME+" Class  -----------------------------------------------------");
1389    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1390    helpMsg_.append("--- + $Revision: 1091 $ $Date: 2016-05-29 16:26:54 -0700 (Sun, 29 May 2016) $ ---");
1391    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1392    helpMsg_.append("-------------------------------------------------------------------------------");
1393    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1394    helpMsg_.append("WebARTS ca.bc.webarts.tools.isy.ISYRestRequester Class");
1395    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1396    helpMsg_.append("SYNTAX:");
1397    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1398    helpMsg_.append("   java ");
1399    helpMsg_.append(CLASSNAME);
1400    helpMsg_.append(" command or {restCommand}");
1401    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1402    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1403    helpMsg_.append("Available commands:");
1404    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1405    helpMsg_.append("    test");
1406    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1407    helpMsg_.append("    listNodes ");
1408    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1409    helpMsg_.append("    listScenes ");
1410    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1411    helpMsg_.append("    listScenesAndNodes ");
1412    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1413    helpMsg_.append("    status noneName");
1414    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1415    helpMsg_.append("    listVars");
1416    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1417    helpMsg_.append("    getVar varName");
1418    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1419    helpMsg_.append("    getProperty nodeName propertyName");
1420    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1421    helpMsg_.append("    toggle nodeName");
1422    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1423    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1424    helpMsg_.append("Available restCommands:");
1425    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1426    helpMsg_.append("    see: http://wiki.universal-devices.com/index.php?title=ISY_Developers:API:REST_Interface");
1427    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1428    helpMsg_.append("  Example: java ca.bc.webarts.android.ISYRestRequester /sys ");
1429    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1430    helpMsg_.append("---------------------------------------------------------");
1431    helpMsg_.append("----------------------");
1432    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1433
1434    return helpMsg_;
1435  }
1436
1437  public static final String TVDIM_ADDR = "42032";
1438  public static final String GAMESROOMLIGHTS_NODENAME = "Game"; // "26534";
1439  public static final String GAMESROOMLIGHTS_ADDR = "15 39 FB 1"; // "26534";
1440  public static final String DWNKITCHEN_NODENAME = "DwnKitchen";
1441  public static final String DWNKITCHEN_ADDR = "16 45 A4 1";
1442  public static final String THERMOSTAT_ADDR = "14 13 C6 1";
1443  public static final String FAMILYMAINLIGHTS_ADDR = "13 A8 32 1";
1444  public static final String VALENCEFRONTLIGHTS_ADDR = "16 43 93 1";
1445  public static final String VALENCESURROUNDLIGHTS_ADDR = "16 46 AE 1";
1446}
1447
1448
1449/* ********************************************************************* */
1450
1451/** An object to represent a ISY Node with name, Address, type, folder, and group - to simply help with name lookups.
1452 *  NO rest requests or call to the ISy are initiated from this class.
1453 **/
1454class IsyDeviceProperty extends Object
1455{
1456  String id_ = "";
1457  String value_ = "";
1458  String formatted_ = "";
1459  String uom_ = "";
1460  boolean isInteger_ = false;
1461  int intValue_=-1;
1462  String elementStr_ = "";
1463
1464  public IsyDeviceProperty()
1465  {
1466  }
1467
1468
1469  public IsyDeviceProperty(String id,String value,String formatted,String uom)
1470  {
1471    id_ = id;
1472    value_ = value;
1473    formatted_ = formatted;
1474    uom_ = uom;
1475  }
1476
1477
1478    /**
1479     * Reads in Property values from the the XML property element from the rest response.
1480     *
1481     *<pre>
1482     *        <property id="ST" value="0" formatted="Off" uom="%/on/off"/>
1483     *</pre>
1484     **/
1485    public IsyDeviceProperty(String properyXmlStr)
1486    {
1487      int valueVal = -1;
1488      int formattedVal = -1;
1489      /* Parse Out id */
1490      int iSpot = properyXmlStr.indexOf(" id");
1491      String iVal = properyXmlStr.substring(iSpot+5,properyXmlStr.indexOf("\"", iSpot+6));
1492      setId(iVal);
1493
1494      /* Parse Out value */
1495      int vSpot = properyXmlStr.indexOf(" value");
1496      String vVal = properyXmlStr.substring(vSpot+8,properyXmlStr.indexOf("\"", vSpot+9));
1497      if(vVal.equalsIgnoreCase("Off") ) valueVal = 0;
1498      else if (vVal.equalsIgnoreCase("On") ) valueVal = 100;
1499      else if (vVal.trim().equals("") ) valueVal = -1;
1500      else if (vVal.trim().indexOf(".")!=-1 )
1501        try{valueVal = Integer.parseInt(vVal.substring(0,vVal.indexOf(".")));}
1502        catch(Exception ex){System.out.println("ERROR pulling the value from "+vVal+" "+vVal.substring(0,vVal.indexOf(".")));}
1503      else
1504        try{valueVal = Integer.parseInt(vVal);}
1505        catch(Exception ex){System.out.println("ERROR pulling the int value from "+vVal);}
1506      setValue(vVal);
1507
1508      /* Parse Out formatted */
1509      int fSpot = properyXmlStr.indexOf(" formatted");
1510      String fVal = properyXmlStr.substring(fSpot+12,properyXmlStr.indexOf("\"", fSpot+13));
1511      if(fVal.equalsIgnoreCase("Off") ) formattedVal = 0;
1512      else if (fVal.equalsIgnoreCase("On") ) formattedVal = 100;
1513      else if (fVal.trim().equals("") ) formattedVal = -1;
1514      else if (fVal.trim().indexOf(".")!=-1 )
1515        try{formattedVal = Integer.parseInt(fVal.substring(0,fVal.indexOf(".")));}
1516        catch(Exception ex){System.out.println("ERROR pulling the formattedValue from "+fVal+" "+fVal.substring(0,fVal.indexOf(".")));}
1517      else
1518        try{formattedVal = Integer.parseInt(fVal);}
1519        catch(Exception ex){System.out.println("ERROR pulling the int vformattedValue from "+fVal);}
1520      setFormatted(fVal);
1521      if(formattedVal!=-1) setIntValue(formattedVal);
1522
1523      /* Parse Out uom */
1524      int uSpot = properyXmlStr.indexOf(" uom");
1525      String uVal = properyXmlStr.substring(uSpot+6,properyXmlStr.indexOf("\"", uSpot+7));
1526      setUom(uVal);
1527    }
1528
1529
1530    public String getElementStr()
1531    {
1532      return "<property id=\""+getId()+"\" value=\""+getValue()+"\" formatted=\""+getFormatted()+"\" uom=\""+getUom()+"\"/>";
1533    }
1534
1535
1536  /**
1537    * Set Method for class field 'id_'.
1538    *
1539    * @param name_ is the value to set this class field to.
1540    *
1541    **/
1542  public  void setId(String id)
1543  {
1544    this.id_ = id;
1545  }  // setId Method
1546
1547
1548  /**
1549    * Get Method for class field 'id_'.
1550    *
1551    * @return String - The value the class field 'id_'.
1552    *
1553    **/
1554  public String getId()
1555  {
1556    return id_;
1557  }  // getId Method
1558
1559
1560  /**
1561    * Set Method for class field 'value_'.
1562    *
1563    * @param value_ is the value to set this class field to.
1564    *
1565    **/
1566  public  void setValue(String value)
1567  {
1568    this.value_ = value;
1569  }  // setValue Method
1570
1571
1572  /**
1573    * Get Method for class field 'value_'.
1574    *
1575    * @return String - The value the class field 'value_'.
1576    *
1577    **/
1578  public String getValue()
1579  {
1580    return value_;
1581  }  // getValue Method
1582
1583  /**
1584    * Set Method for class field 'formatted_'.
1585    *
1586    * @param formatted_ is the value to set this class field to.
1587    *
1588    **/
1589  public  void setFormatted(String formatted)
1590  {
1591    this.formatted_ = formatted;
1592  }  // setFormatted Method
1593
1594
1595  /**
1596    * Get Method for class field 'formatted_'.
1597    *
1598    * @return String - The value the class field 'formatted_'.
1599    *
1600    **/
1601  public String getFormatted()
1602  {
1603    return formatted_;
1604  }  // getFormatted Method
1605
1606
1607  /**
1608    * Set Method for class field 'uom_'.
1609    *
1610    * @param uom_ is the value to set this class field to.
1611    *
1612    **/
1613  public  void setUom(String uom)
1614  {
1615    this.uom_ = uom;
1616  }  // setUom Method
1617
1618
1619  /**
1620    * Get Method for class field 'uom_'.
1621    *
1622    * @return String - The value the class field 'uom_'.
1623    *
1624    **/
1625  public String getUom()
1626  {
1627    return uom_;
1628  }  // getUom Method
1629
1630
1631  /**
1632    * Set Method for class field 'isInteger_'.
1633    *
1634    * @param isInteger_ is the value to set this class field to.
1635    *
1636    **/
1637  public  void setIsInteger(boolean isInteger)
1638  {
1639    this.isInteger_ = isInteger;
1640  }  // setIsInteger Method
1641
1642
1643  /**
1644    * Get Method for class field 'isInteger_'.
1645    *
1646    * @return boolean - The value the class field 'isInteger_'.
1647    *
1648    **/
1649  public boolean getIsInteger()
1650  {
1651    return isInteger_;
1652  }  // getIsInteger Method
1653
1654
1655  /**
1656    * Set Method for class field 'intValue_'.
1657    *
1658    * @param intValue_ is the value to set this class field to.
1659    *
1660    **/
1661  public  void setIntValue(int intValue)
1662  {
1663    this.intValue_ = intValue;
1664    if (intValue!=-1) setIsInteger(true);
1665  }  // setIntValue Method
1666
1667
1668  /**
1669    * Get Method for class field 'intValue_'.
1670    *
1671    * @return int - The value the class field 'intValue_'.
1672    *
1673    **/
1674  public int getIntValue()
1675  {
1676    return intValue_;
1677  }  // getIntValue Method
1678
1679}
1680
1681
1682/* ********************************************************************* */
1683
1684/** An object to represent a ISY Node with name, Address, type, folder, and group - to simply help with name lookups.
1685 *  NO rest requests or call to the ISy are initiated from this class.
1686 **/
1687class IsyNodes extends Object
1688{
1689  public static final int SWITCH_TYPE = 1;
1690  public static final int DIMMER_TYPE = 2;
1691  public static final int PLUG_TYPE = 3;
1692  public static final int SENSOR_TYPE = 4;
1693  public static final int THERMOSTAT_TYPE = 5;
1694  public static final int METER_TYPE = 6;
1695  Document nodeDoc_ = null;
1696  Element rootElem_ = null;
1697  Builder xmlBuilder_ = new Builder();
1698  String [] elementNames_ = {"root","folder","node","group"};
1699  String [] nodeNames_ = null;
1700  String [] nodeAddresses_  = null;
1701  int    [] nodeTypes_ = null;
1702  String [] folderNames_ = null;
1703  String [] folderAddresses_ = null;
1704  String [] groupNames_ = null;
1705  String [] groupAddresses_ = null;
1706  GroupMembers [] groupMembers_ = null;
1707  Elements folderElements_ = null;
1708  Elements nodeElements_ = null;
1709  Elements groupElements_ = null;
1710
1711  public IsyNodes()
1712  {
1713    /*
1714    StringBuilder nodeXmlSB = ISYRestRequester.serviceGet("/nodes");
1715    if(nodeXmlSB!=null && nodeXmlSB.length()>7)
1716    {
1717      nodeDoc_ = parseXMLNodes(nodeXmlSB.toString());
1718    }
1719    */
1720  }
1721
1722
1723  public IsyNodes(String nodeXmlStr) throws ParsingException, IOException
1724  {
1725    if(nodeXmlStr!=null && nodeXmlStr.length()>7)
1726    {
1727
1728      nodeDoc_ = parseXMLNodes(nodeXmlStr);
1729    }
1730  }
1731
1732
1733  public IsyNodes(StringBuilder nodeXmlSB) throws ParsingException, IOException
1734  {
1735    if(nodeXmlSB!=null && nodeXmlSB.length()>7)
1736    {
1737      nodeDoc_ = parseXMLNodes(nodeXmlSB.toString());
1738    }
1739  }
1740
1741
1742  public String toString()
1743  {
1744    String retVal = "";
1745    for (int i=0; i<nodeNames_.length; i++)
1746    {
1747      retVal+=nodeNames_[i];
1748      retVal+=" {";
1749      retVal+=nodeTypes_[i];
1750      retVal+=") : ";
1751      retVal+= getNodeAddress(nodeNames_[i]);
1752      retVal+="\n";
1753    }
1754    return retVal;
1755  }
1756
1757
1758  protected boolean hasNodeName(String nodeName)
1759  {
1760    boolean retVal = false;
1761    //System.out.println("Checking ");
1762    for(int i=0; i< nodeNames_.length;i++)
1763    {
1764      //System.out.print("    "+nodeNames_[i]);
1765      if(nodeNames_[i].equalsIgnoreCase(nodeName)) { retVal = true;i=nodeNames_.length;}
1766      //System.out.println("  "+retVal);
1767    }
1768    return retVal;
1769  }
1770
1771
1772  protected boolean hasGroupName(String groupName)
1773  {
1774    boolean retVal = false;
1775    //System.out.println("Checking ");
1776    for(int i=0; i< groupNames_.length;i++)
1777    {
1778      //System.out.print("    "+nodeNames_[i]);
1779      if(groupNames_[i].equalsIgnoreCase(groupName)) { retVal = true;i=groupNames_.length;}
1780      //System.out.println("  "+retVal);
1781    }
1782    return retVal;
1783  }
1784
1785  protected boolean hasSceneName(String sceneName)
1786  {
1787    return hasGroupName(sceneName);
1788  }
1789
1790
1791  /** Returns the class var nodeNames_. **/
1792  protected String [] getNodeNames()
1793  {
1794    return nodeNames_;
1795  }
1796
1797
1798  /** Returns the class var nodeAddresses_. **/
1799  protected String [] getNodeAddresses()
1800  {
1801    return nodeAddresses_;
1802  }
1803
1804
1805  /** Returns a verbatim COPY of the class var nodeNames_. **/
1806  protected String [] getNodeNamesCopy(){ return getNodeNamesCopy(false);}
1807  /** Returns a verbatim COPY of the class var nodeNames_. **/
1808  protected String [] getNodeNamesCopy(boolean sorted)
1809  {
1810    String [] retVal = new String[nodeNames_.length];
1811    java.lang.System.arraycopy(nodeNames_, 0, retVal, 0, nodeNames_.length);
1812    if(sorted) Quick.sort(retVal);
1813    return retVal;
1814  }
1815
1816
1817  /** Returns a verbatim COPY of the class var sceneNames_. **/
1818  protected String [] getSceneNamesCopy(){ return getSceneNamesCopy(false);}
1819  /** Returns a verbatim COPY of the class var sceneNames_. **/
1820  protected String [] getSceneNamesCopy(boolean sorted)
1821  {
1822    return getGroupNamesCopy(sorted);
1823  }
1824
1825
1826  /** Returns a verbatim COPY of the class var nodeAddresses_. **/
1827  protected String [] getNodeAddressesCopy()
1828  {
1829    String [] retVal = new String[nodeAddresses_.length];
1830    java.lang.System.arraycopy(nodeAddresses_, 0, retVal, 0, nodeAddresses_.length);
1831    return retVal;
1832  }
1833
1834
1835  /** Returns a verbatim COPY of the class var groupAddresses_. **/
1836  protected String [] getSceneAddressesCopy()
1837  {
1838    return getGroupAddressesCopy();
1839  }
1840
1841
1842  /** Returns the class var folderNames_. **/
1843  protected String [] getFolderNames()
1844  {
1845    return folderNames_;
1846  }
1847
1848
1849  /** Returns a verbatim COPY of the class var folderNames_. **/
1850  protected String [] getFolderNamesCopy(){ return getNodeNamesCopy(false);}
1851  /** Returns a verbatim COPY of the class var folderNames_. **/
1852  protected String [] getFolderNamesCopy(boolean sorted)
1853  {
1854    String [] retVal = new String[folderNames_.length];
1855    java.lang.System.arraycopy(folderNames_, 0, retVal, 0, folderNames_.length);
1856    if(sorted) Quick.sort(retVal);
1857    return retVal;
1858  }
1859
1860
1861   /** Returns the class var groupNames_. **/
1862  protected String [] getGroupNames()
1863  {
1864    return groupNames_;
1865  }
1866
1867
1868  /** Returns a verbatim COPY of the class var groupNames_. **/
1869  protected String [] getGroupNamesCopy(){ return getNodeNamesCopy(false);}
1870  /** Returns a verbatim COPY of the class var groupNames_. **/
1871  protected String [] getGroupNamesCopy(boolean sorted)
1872  {
1873    String [] retVal = new String[groupNames_.length];
1874    java.lang.System.arraycopy(groupNames_, 0, retVal, 0, groupNames_.length);
1875    if(sorted) Quick.sort(retVal);
1876    return retVal;
1877  }
1878
1879
1880  /** Returns a verbatim COPY of the class var groupAddresses_. **/
1881  protected String [] getGroupAddressesCopy()
1882  {
1883    String [] retVal = new String[groupAddresses_.length];
1884    java.lang.System.arraycopy(groupAddresses_, 0, retVal, 0, groupAddresses_.length);
1885    return retVal;
1886  }
1887
1888
1889  protected Element getNodeWithName(String nodeName)
1890  {
1891    Element retVal = null;
1892    if (nodeNames_!=null && nodeNames_.length>0)
1893    {
1894      int i=0;
1895      while( retVal==null  && i<nodeNames_.length)
1896      {
1897        if(nodeNames_[i].equals(nodeName)) retVal=nodeElements_.get(i);
1898        i++;
1899      }
1900    }
1901    return retVal;
1902  }
1903
1904
1905  protected Element getSceneWithName(String sceneName)
1906  {
1907    Element retVal = null;
1908    if (groupNames_!=null && groupNames_.length>0)
1909    {
1910      int i=0;
1911      while( retVal==null  && i<groupNames_.length)
1912      {
1913        if(groupNames_[i].equals(sceneName)) retVal=groupElements_.get(i);
1914        i++;
1915      }
1916    }
1917    return retVal;
1918  }
1919
1920
1921  protected String getSceneAddress(String sceneName)  { return getGroupAddress(sceneName); }
1922  protected String getGroupAddress(String sceneName)
1923  {
1924    String retVal = "";
1925    if (groupNames_!=null && groupNames_.length>0)
1926    {
1927      //System.out.print("\n   >>> getGroupAddress( "+sceneName+" )  ");
1928      //System.out.println("\n       groupNames_.length="+groupNames_.length);
1929      int i=0;
1930      while( retVal.equals("") && i<groupNames_.length )
1931      {
1932        if(groupNames_[i].equalsIgnoreCase(sceneName))
1933        {
1934          retVal=groupAddresses_[i];
1935          //System.out.print(""+i+"] "+groupNames_[i]+"="+retVal);
1936        }
1937        i++;
1938      }
1939    }
1940    //System.out.print("\n");
1941    return retVal;
1942  }
1943
1944
1945  protected String getNodeAddress(String nodeName)
1946  {
1947    String retVal = "";
1948    if (nodeNames_!=null && nodeNames_.length>0)
1949    {
1950      //System.out.print("\n   >>> getNodeAddress( "+nodeName+" )  ");
1951      //System.out.println("\n       nodeNames_.length="+nodeNames_.length);
1952      int i=0;
1953      while( retVal.equals("") && i<nodeNames_.length )
1954      {
1955        if(nodeNames_[i].equalsIgnoreCase(nodeName))
1956        {
1957          retVal=nodeAddresses_[i];
1958          //System.out.print(""+i+"] "+nodeNames_[i]+"="+retVal);
1959        }
1960        i++;
1961      }
1962    }
1963    // not found in NODES, look in groups
1964    if("".equals(retVal))
1965    {
1966      retVal = getGroupAddress(nodeName);
1967    }
1968    //System.out.print("\n");
1969    return retVal;
1970  }
1971
1972
1973  protected int getNodeRef(String nodeName)
1974  {
1975    int retVal=-1;
1976    int i=0;
1977    if (nodeNames_!=null && nodeNames_.length>0)
1978    {
1979      //System.out.print("\n   >>> getNodeAddress( "+nodeName+" )  ");
1980      //System.out.println("\n       nodeNames_.length="+nodeNames_.length);
1981      while( retVal==-1 && i<nodeNames_.length )
1982      {
1983        if(nodeNames_[i].equalsIgnoreCase(nodeName))
1984        {
1985          retVal=i;
1986          //System.out.print(""+i+"] "+nodeNames_[i]+"="+retVal);
1987        }
1988        i++;
1989      }
1990    }
1991    return i;
1992  }
1993
1994
1995  /** gets the array ref num for the given scene Name.
1996    *
1997    * @return the ref num for the array OR -1 if scene not found
1998    **/
1999  protected int getGroupRef(String sceneName)
2000  {
2001    int retVal=-1;
2002    int i=0;
2003    if (groupNames_!=null && groupNames_.length>0)
2004    {
2005      //System.out.print("\n   >>> getGroupAddress( "+sceneName+" )  ");
2006      //System.out.println("\n       groupNames_.length="+groupNames_.length);
2007      while( retVal==-1 && i<groupNames_.length )
2008      {
2009        if(groupNames_[i].equalsIgnoreCase(sceneName))
2010        {
2011          retVal=i;
2012          //System.out.print(""+i+"] "+groupNames_[i]+"="+retVal);
2013        }
2014        i++;
2015      }
2016    }
2017    //System.out.print("\n");
2018    return retVal;
2019  }
2020
2021
2022  GroupMembers getGroupMembers(String sceneName)
2023  {
2024    GroupMembers retVal = null;
2025    if(getGroupRef(sceneName)>-1) retVal = groupMembers_[getGroupRef(sceneName)];
2026    return retVal;
2027  }
2028
2029
2030  protected int getNodeType(String nodeName)
2031  {
2032    int retVal = -1;
2033    if (nodeNames_!=null && nodeNames_.length>0)
2034    {
2035      int i=0;
2036      while( retVal==-1  && i<nodeNames_.length)
2037      {
2038        if(nodeNames_[i].equals(nodeName)) retVal=nodeTypes_[i];
2039        i++;
2040      }
2041    }
2042    return retVal;
2043  }
2044
2045
2046  protected String getNodeTypeStr(String nodeName)
2047  {
2048    String retVal = "";
2049    int t = getNodeType( nodeName);
2050    if (t==SWITCH_TYPE) retVal="SWITCH_TYPE";
2051    else if (t==DIMMER_TYPE) retVal="DIMMER_TYPE";
2052    else if (t==PLUG_TYPE) retVal="PLUG_TYPE";
2053    else if (t==SENSOR_TYPE) retVal="SENSOR_TYPE";
2054    else if (t==THERMOSTAT_TYPE) retVal="THERMOSTAT_TYPE";
2055    else if (t==METER_TYPE) retVal="METER_TYPE";
2056    return retVal;
2057  }
2058
2059
2060  protected String getNodeName(String nodeAddress)
2061  {
2062    String retVal = "";
2063    if (nodeAddresses_!=null && nodeAddresses_.length>0)
2064    {
2065      int i=0;
2066      while( retVal.equals("")  && i<nodeAddresses_.length)
2067      {
2068        if(nodeAddresses_[i].equals(nodeAddress)) retVal=nodeNames_[i];
2069        i++;
2070      }
2071    }
2072    return retVal;
2073  }
2074
2075
2076  protected String getGroupName(String sceneAddress) { return getSceneName(sceneAddress);}
2077  protected String getSceneName(String sceneAddress)
2078  {
2079    String retVal = "";
2080    if (groupAddresses_!=null && groupAddresses_.length>0)
2081    {
2082      int i=0;
2083      while( retVal.equals("")  && i<groupAddresses_.length)
2084      {
2085        if(groupAddresses_[i].equals(sceneAddress)) retVal=groupNames_[i];
2086        i++;
2087      }
2088    }
2089    return retVal;
2090  }
2091
2092
2093    /**
2094     * Reads in the XML Config file.
2095     *
2096     *<pre>
2097     *    <node flag="128">
2098     *      <address>1 81 CF 1</address>
2099     *      <name>HangingLight-E</name>
2100     *      <parent type="3">28566</parent>
2101     *      <type>1.7.55.0</type>
2102     *      <enabled>true</enabled>
2103     *      <deviceClass>0</deviceClass>
2104     *      <wattage>0</wattage>
2105     *      <dcPeriod>0</dcPeriod>
2106     *      <pnode>1 81 CF 1</pnode>
2107     *      <ELK_ID>C07</ELK_ID>
2108     *      <property id="ST" value="0" formatted="Off" uom="%/on/off" />
2109     *    </node>
2110     *</pre>
2111     **/
2112    protected nu.xom.Document parseXMLNodes(String nodeXmlStr) throws ParsingException, IOException
2113    {
2114      //String methodName = className_+"."+Util.getCurrentMethodName();
2115      //log_.startMethod(methodName);
2116      try
2117      {
2118        nodeDoc_ = xmlBuilder_.build(nodeXmlStr,null);
2119      }
2120      catch (nu.xom.ParsingException xomEx)
2121      {
2122        xomEx.printStackTrace();
2123        System.out.println("MAJor Error: parsing nodeXmlStr");
2124        System.out.println(nodeXmlStr);
2125        System.out.println("MAJor Error: parsing nodeXmlStr");
2126      }
2127      if (nodeDoc_!=null)
2128      {
2129        rootElem_ = nodeDoc_.getRootElement();
2130        // load the data
2131        if (rootElem_ != null && rootElem_ instanceof Element && rootElem_.getLocalName().equals("nodes"))
2132        {
2133          folderElements_ = rootElem_.getChildElements("folder");
2134          nodeElements_ = rootElem_.getChildElements("node");
2135          groupElements_ = rootElem_.getChildElements("group");
2136
2137          Element currE = null;
2138          Element stateE = null;
2139          int numNodes = nodeElements_.size();
2140          if (nodeElements_!=null && nodeElements_.size()>0)
2141          {
2142            nodeNames_ = new String[numNodes];
2143            nodeAddresses_ = new String[numNodes];
2144            nodeTypes_ = new int[numNodes];
2145
2146            //System.out.println("\nParsing Nodes:");
2147            for (int i=0; i< numNodes; i++)
2148            {
2149              currE = null;
2150              currE = nodeElements_.get(i);
2151              if (currE !=null)
2152              {
2153                try
2154                {
2155                  nodeNames_[i] = currE.getFirstChildElement("name").getValue();
2156                  nodeAddresses_[i] = currE.getFirstChildElement("address").getValue();
2157
2158                  //System.out.println("  > "+nodeNames_[i]+"  "+nodeAddresses_[i]);
2159
2160                  stateE = currE.getFirstChildElement("property");  // should be the 'ST' property
2161                  if(nodeNames_[i].toUpperCase().indexOf("PLUG")!=-1){ nodeTypes_[i] = PLUG_TYPE;}
2162                  else if(nodeNames_[i].toUpperCase().indexOf("SENSE")!=-1){nodeTypes_[i] = SENSOR_TYPE;}
2163                  else if(nodeNames_[i].contains("Thermostat")||
2164                          (stateE!=null&&stateE.getAttribute("uom").getValue().equals("degrees")) )
2165                         { nodeTypes_[i] = THERMOSTAT_TYPE;}
2166                  else if((stateE!=null&&stateE.getAttribute("uom").getValue().indexOf("%")!=-1 )) { nodeTypes_[i] = DIMMER_TYPE;}
2167                  else if((stateE!=null&&stateE.getAttribute("uom").getValue().equals("W") )) { nodeTypes_[i] = METER_TYPE;}
2168                  else { nodeTypes_[i] = SWITCH_TYPE;}
2169                }
2170                catch(java.lang.NullPointerException npEx)
2171                {
2172                  System.out.println("Minor Error: problem parsing "+i+"\n     nodeName: "+nodeNames_[i]+"\n     nodeAddresses_"+nodeAddresses_[i]);
2173                  System.out.println("             child Element "+stateE);
2174                }
2175             }
2176            }
2177          }
2178          if (folderElements_!=null && folderElements_.size()>0)
2179          {
2180            folderNames_ = new String[folderElements_.size()];
2181            folderAddresses_ = new String[folderElements_.size()];
2182            for (int i=0; i< folderElements_.size(); i++)
2183            {
2184              currE = null;
2185              currE = folderElements_.get(i);
2186              if (currE !=null)
2187              {
2188                folderNames_[i] = currE.getFirstChildElement("name").getValue();
2189                folderAddresses_[i] = currE.getFirstChildElement("address").getValue();
2190             }
2191            }
2192          }
2193          if (groupElements_!=null && groupElements_.size()>0)
2194          {
2195            groupNames_ = new String[groupElements_.size()];
2196            groupAddresses_ = new String[groupElements_.size()];
2197            groupMembers_ = new GroupMembers[groupElements_.size()];
2198            for (int i=0; i< groupElements_.size(); i++)
2199            {
2200              currE = null;
2201              currE = groupElements_.get(i);
2202              if (currE !=null)
2203              {
2204                Element members = null;
2205                Elements links = null;
2206                try
2207                {
2208                  groupNames_[i] = currE.getFirstChildElement("name").getValue();
2209                  groupAddresses_[i] = currE.getFirstChildElement("address").getValue();
2210                  // the group nodes are wrapped in:
2211                  /*
2212                    <members>
2213                      <link type="16">13 EB 11 1</link>
2214                      <link type="16">43 A2 FB 3</link>
2215                      <link type="32">13 57 A7 1</link>
2216                      <link type="32">20 B8 93 1</link>
2217                    </members>
2218                    // type is a relationship type
2219                    // 16 is a switch/controller
2220                    // 32 is a node
2221                  */
2222                  members = currE.getFirstChildElement("members");
2223                  links = members.getChildElements("link");
2224                  groupMembers_[i] = new GroupMembers(links);
2225                }
2226                catch(java.lang.NullPointerException npEx)
2227                {
2228                  System.out.println("Minor Error: problem parsing "+i+"\n     groupName: "+groupNames_[i]+"\n     groupAddresses: "+groupAddresses_[i]);
2229                  System.out.println("             members Element "+members);
2230                  System.out.println("             links "+links);
2231                  System.out.println("             currE "+currE);
2232                  npEx.printStackTrace();
2233                }
2234              }
2235            }
2236          }
2237        }
2238      }
2239
2240      //log_.endMethod(methodName);
2241      return nodeDoc_;
2242    }
2243}
2244
2245/* ********************************************************************* */
2246
2247/* holds the addresses of scene/group member nodes and switches */
2248class GroupMembers extends Object
2249{
2250  String [] memberNodes_ = null;
2251  String [] memberSwitchNodes_ = null;
2252  boolean debugOut_ = false;
2253
2254  public GroupMembers(Elements links)
2255  {
2256    int devCount = 0;
2257    int swCount = 0;
2258    for (int i = 0; i<links.size(); i++)
2259    {
2260      // ONLY include switchable nodes type='32'
2261      try
2262      {
2263        if("32".equals(links.get(i).getAttribute("type").getValue()))  devCount++;
2264        else swCount++;
2265      }
2266      catch(java.lang.NullPointerException npEx) {if(debugOut_) npEx.printStackTrace();}
2267    }
2268    memberNodes_ = new String[devCount];
2269    memberSwitchNodes_ = new String[swCount];
2270    devCount = 0;
2271    swCount = 0;
2272    for (int i = 0; i<links.size(); i++)
2273    {
2274      // ONLY include switchable nodes type='32'
2275      try
2276      {
2277        if("32".equals(links.get(i).getAttribute("type").getValue()))
2278        {
2279          memberNodes_[devCount++] = links.get(i).getValue();
2280        }
2281        else
2282        {
2283          memberSwitchNodes_[swCount++] = links.get(i).getValue();
2284        }
2285      }
2286      catch(java.lang.NullPointerException npEx) {if(debugOut_) npEx.printStackTrace();}
2287    }
2288
2289  }// constructor
2290
2291  String [] getMemberNodes() { return memberNodes_;}
2292  String [] getMemberSwitchNodes() { return memberSwitchNodes_;}
2293  public String toString()
2294  {
2295    String retVal = "node addresses: ";
2296    for(String memAddr : memberNodes_) retVal += memAddr+", ";
2297    retVal=retVal.substring(0,retVal.length()-2);
2298    retVal += System.getProperty("line.separator")+"switch/controller addresses: ";
2299    for(String memAddr : memberSwitchNodes_) retVal += memAddr+", ";
2300    retVal=retVal.substring(0,retVal.length()-2);
2301    return retVal;
2302  }
2303} // class GroupMembers
2304
2305
2306/* ********************************************************************* */
2307
2308
2309/** an object to represent an ISY Node. **/
2310class IsyVars extends Object
2311{
2312  public static final int VAR_TYPE_INT = 1;
2313  public static final int VAR_TYPE_STATE = 2;
2314  int varType_ = VAR_TYPE_INT;
2315  int maxID_ = 0;
2316  Document varDoc_ = null;
2317  Document varValueDoc_ = null;
2318  Element rootElem_ = null;
2319  Builder xmlBuilder_ = new Builder();
2320  Hashtable <String, Integer>  varName_ = null;
2321  String [] varValue_ = null;
2322  Elements varElements_ = null;
2323
2324
2325  public IsyVars()
2326  {
2327
2328  }
2329
2330
2331  public IsyVars( int varType, StringBuilder varXmlSB, StringBuilder varValuesXmlSB) throws ParsingException, IOException
2332  {
2333    if(  (varType==VAR_TYPE_INT || varType==VAR_TYPE_STATE) && varXmlSB!=null && varXmlSB.length()>7 && varValuesXmlSB!=null && varValuesXmlSB.length()>7 )
2334    {
2335      varType_ = varType;
2336      parseXMLVars(varXmlSB.toString(),varValuesXmlSB.toString());
2337    }
2338  }
2339
2340
2341  /**
2342   * Reads in the XML Config files - a definition XML and a var Value XML.
2343   *
2344   *<pre>
2345   * <CList type="VAR_INT">
2346   *     <e id="1" name="ir9Count" />
2347   *     <e id="2" name="Int_UpKeypadTemp" />
2348   *     <e id="3" name="Int_DwnKeypadTemp" />
2349   *     <e id="4" name="VacationMode" />
2350   *     <e id="5" name="iMeterCurrentValue" />
2351   *     <e id="6" name="PowerFailure" />
2352   *     <e id="7" name="LastPowerFailure" />
2353   *     <e id="8" name="ChristmasMode" />
2354   *     <e id="9" name="StairSensor" />
2355   *     <e id="10" name="PowerFailureEmailSent" />
2356   *     <e id="11" name="WakeWarp4" />
2357   *     <e id="12" name="tomsMorningLights" />
2358   *     <e id="13" name="iDay.Of.Year" />
2359   *     <e id="14" name="iDay.Of.Month" />
2360   *     <e id="15" name="iYear" />
2361   *     <e id="17" name="iDay.Of.Week" />
2362   *     <e id="16" name="iLeap.Year" />
2363   *     <e id="19" name="iDay.Counter" />
2364   *     <e id="18" name="iMonth" />
2365   *     <e id="21" name="iEvery.Third.Day.Counter" />
2366   *     <e id="20" name="iEvery.Other.Day.Counter" />
2367   *     <e id="23" name="iEvery.Fifth.Day.Counter" />
2368   *     <e id="22" name="iEvery.Fourth.Day.Counter" />
2369   *     <e id="25" name="iWeek.Counter" />
2370   *     <e id="24" name="iWeek.Of.Month" />
2371   *     <e id="27" name="iEvery.Third.Week.Counter" />
2372   *     <e id="26" name="iEvery.Other.Week.Counter" />
2373   *     <e id="29" name="iOdd.Even.Day" />
2374   *     <e id="28" name="iEvery.Fourth.Week.Counter" />
2375   *     <e id="31" name="iSync" />
2376   *     <e id="30" name="iHoliday" />
2377   *     <e id="34" name="elk.zone.down.window.violated" />
2378   *     <e id="35" name="elk.zone.water.violated" />
2379   *     <e id="32" name="elk.zone.door.violated" />
2380   *     <e id="33" name="elk.zone.up.window.violated" />
2381   *     <e id="36" name="elk.zone.smoke.violated" />
2382   *     <e id="37" name="elk.zone.up.bedroomWindows.bypass" />
2383   * </CList>
2384   *
2385   *  <vars>
2386   *      <var type="1" id="1">
2387   *        <init>0</init>
2388   *        <val>0</val>
2389   *        <ts>20160131 17:15:16</ts>
2390   *      </var>
2391   *      <var type="1" id="2">
2392   *        <init>0</init>
2393   *        <val>22</val>
2394   *        <ts>20160313 09:58:26</ts>
2395   *      </var>
2396   *      ....
2397   *   </vars>
2398   *</pre>
2399   **/
2400  protected void parseXMLVars(String varXmlStr, String varValuesXmlStr) throws ParsingException, IOException
2401  {
2402    //String methodName = className_+"."+Util.getCurrentMethodName();
2403    //log_.startMethod(methodName);
2404    String tmpNm = "";
2405    String tmpId = "";
2406    varDoc_ = xmlBuilder_.build(varXmlStr,null);
2407    if (varDoc_!=null)
2408    {
2409      rootElem_ = varDoc_.getRootElement();
2410      // load the var definitions
2411      if (rootElem_ != null && rootElem_ instanceof Element && rootElem_.getLocalName().equals("CList"))
2412      {
2413        varElements_ = rootElem_.getChildElements("e");
2414
2415        Element currE = null;
2416        if (varElements_!=null && varElements_.size()>0)
2417        {
2418          varName_ = new Hashtable<String, Integer>();
2419          for (int i=0; i< varElements_.size(); i++)
2420          {
2421            currE = varElements_.get(i);
2422            if (currE !=null)
2423            {
2424              tmpNm = currE.getAttribute("name").getValue();
2425              tmpId = currE.getAttribute("id").getValue().trim();
2426              //System.out.println("Adding "+tmpNm+":"+tmpId);
2427              varName_.put(tmpNm, new Integer(tmpId));
2428              if(Integer.parseInt(tmpId)>maxID_) maxID_ = Integer.parseInt(tmpId);
2429           }
2430          }
2431        }
2432      }
2433    }
2434    varValueDoc_ = xmlBuilder_.build(varValuesXmlStr,null);
2435    if (varDoc_!=null)
2436    {
2437      rootElem_ = varValueDoc_.getRootElement();
2438      // load the var definitions
2439      if (rootElem_ != null && rootElem_ instanceof Element && rootElem_.getLocalName().equals("vars"))
2440      {
2441        varElements_ = rootElem_.getChildElements("var");
2442      }
2443    }
2444  }
2445
2446
2447  public int getNumVars()
2448  {
2449    int retVal = 0;
2450    if(varName_!=null) retVal = varName_.size();
2451    return retVal;
2452  }
2453
2454
2455  public String [] getVarNames()
2456  {
2457    String [] retVal = new String[varName_.size()];
2458    Set<String> namesSet = varName_.keySet();
2459    retVal = (String []) namesSet.toArray(retVal);
2460    return retVal;
2461  }
2462
2463
2464  public int getVarValue(String varName) throws Exception
2465  {
2466    int retVal = 0;
2467    //System.out.println(" DEBUG: Varname: "+varName+" VarVal: ");
2468    int varID = 0;
2469    Integer vI = null;
2470
2471    Set<String> namesSet = varName_.keySet();
2472    //System.out.println(Arrays.toString(namesSet.toArray()));
2473    if(varName_!=null && varName_.containsKey(varName))
2474    {
2475      try
2476      {
2477        vI = ((Integer)varName_.get(varName));
2478        if(vI !=null)
2479          varID = vI.intValue();
2480        else
2481          System.out.println("\nCrapped Out on  ("+varName+") "+varName_.get(varName));
2482      }
2483      catch ( java.lang.NullPointerException nEx)
2484      {
2485        System.out.println("\nERROR with "+varName);
2486        nEx.printStackTrace();
2487        throw new Exception("VarName not found");
2488      }
2489    }
2490    else throw new Exception("VarName not found");
2491
2492    if (varID>0)
2493    {
2494      Element currE = null;
2495      Attribute currID = null;
2496      if (varElements_!=null && varElements_.size()>0)
2497      {
2498        // varName_ = new Hashtable<String, Integer>();
2499        for (int i=0; i< varElements_.size(); i++)
2500        {
2501          currE = varElements_.get(i);
2502          if (currE !=null)
2503          {
2504            currID = currE.getAttribute("id");
2505             if( currID!=null && Integer.parseInt(currID.getValue())==varID)
2506             {
2507               retVal =  Integer.parseInt(currE.getFirstChildElement("val").getValue());
2508               i = varElements_.size();
2509             }
2510          }
2511        }
2512      }
2513    }
2514    return retVal;
2515  }
2516
2517
2518  /** the max ID value held as a class var. **/
2519  public int getMaxID()
2520  {
2521    return maxID_;
2522  }
2523
2524
2525  /** the next unused var id value that can be used to create a new var. **/
2526  public int getNextUnusedID()
2527  {
2528    return getMaxID()+1;
2529  }
2530
2531
2532  public String toString()
2533  {
2534    return varName_.toString().replace(", ",",\n ").replace("{","{\n ").replace("}","\n}");
2535  }
2536
2537
2538}