001/*
002 *  $URL: svn://fred.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/eiscp/Eiscp.java $
003 *  $Author: tgutwin $
004 *  $Revision: 1308 $
005 *  $Date: 2020-02-02 10:41:39 -0800 (Sun, 02 Feb 2020) $
006 */
007/*
008 *
009 *  Written by Tom Gutwin - WebARTS Design.
010 *  Copyright (C) 2012-2014 WebARTS Design, North Vancouver Canada
011 *  http://www.webarts.bc.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; either version 2 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.eiscp;
029
030import java.io.*;
031import java.net.*;
032import java.util.HashMap;
033import java.util.TreeSet;
034import java.util.Iterator;
035import java.util.Vector;
036
037/**
038 *  A class that wraps the comunication to Onkyo/Integra devices using the
039 *  ethernet Integra Serial Control Protocal (eISCP). This class uses class
040 *  constants and commandMaps to help handling of the many iscp Commands.
041 *  <br />
042 *  The Message packet looks like:<br />
043 *  <img src="http://tom.webarts.ca/_/rsrc/1320209141605/Blog/new-blog-items/javaeiscp-integraserialcontrolprotocol/eISCP-Packet.png" border="1"/>
044 *  <br /> See also <a href="http://tom.webarts.ca/Blog/new-blog-items/javaeiscp-integraserialcontrolprotocol" > tom.webarts.ca</a> writeup.
045 *
046 * @author     Tom Gutwin P.Eng
047 */
048public class Eiscp
049{
050  /**  A holder for this clients System File Separator.  */
051  public final static String SYSTEM_FILE_SEPERATOR = File.separator;
052
053  /**  A holder for this clients System line termination separator.  */
054  public final static String SYSTEM_LINE_SEPERATOR =
055                                           System.getProperty("line.separator");
056
057  /**  The VM classpath (used in some methods)..  */
058  public static String CLASSPATH = System.getProperty("class.path");
059
060  /**  The users home ditrectory.  */
061  public static String USERHOME = System.getProperty("user.home");
062
063  /**  The users pwd ditrectory.  */
064  public static String USERDIR = System.getProperty("user.dir");
065
066  /**  A holder This classes name (used when logging).  */
067  private static String CLASSNAME = "ca.bc.webarts.tools.eiscp.Eiscp";
068
069  /**  Class flag signifying if the initUtil method has been called  */
070  private static boolean classInit = false;
071
072  /**  Class flag signifying if debugging_ messages are ptinted */
073  private static boolean debugging_ = true;
074
075  /** default receiver IP Address. **/
076  private static final String DEFAULT_EISCP_IP = "10.0.0.203";
077  /** Instantiated class IP for the receiver to communicate with. **/
078  private String receiverIP_ = DEFAULT_EISCP_IP;
079
080  /** default eISCP port. **/
081  private static final int DEFAULT_EISCP_PORT = 60128;
082  /** Instantiated class Port for the receiver to communicate with. **/
083  private int receiverPort_ = DEFAULT_EISCP_PORT;
084
085  /** the socket for communication - the protocol spec says to use one socket connection AND HOLD ONTO IT for re-use.
086    Note: The communication between server and client should hold one connection continuously. Unless it has connected continuously, the notice of status cannot be performed from a client.
087    Note: The number of the connections who can connect with a client is one.
088    Note: The Interval Time Receiving the message needs to take more than 50msec.
089  **/
090  private static Socket eiscpSocket_ = null;
091  /** the timeout in ms for socket reads. **/
092  private static int socketTimeOut_ = 800;
093  private static ObjectOutputStream out_ = null;
094  private static DataInputStream in_ = null;
095  private static boolean connected_ = false;
096
097  private static IscpCommands iscp_ = IscpCommands.getInstance();
098
099  /** Maps the class contant vars to the eiscp command string. **/
100  private static HashMap<Integer, String> commandMap_ = null;
101
102  /** Maps a Readable string to a corresponding class var. **/
103  private static HashMap<String, Integer> commandNameMap_ = null;
104
105  /** Var to hold the volume level to or from a message. **/
106  private static int volume_ = 32;
107
108  private static StringBuffer helpMsg_ = new StringBuffer(SYSTEM_LINE_SEPERATOR);
109
110  /** Simple class Constructor (using deafult IP and port) that gets all the class command constants set-up along with their command lookup maps (commandNameMap_ and commandMap_) . **/
111  public Eiscp()
112  {
113    //initCommandMap();
114  }
115
116
117  /** Constructor that takes your receivers ip and default port, gets all the class command
118   * constants set-up along with their command lookup maps (commandNameMap_ and commandMap_) .
119   **/
120  public Eiscp(String ip)
121  {
122    //initCommandMap();
123    if (ip==null || ip.equals(""))
124      receiverIP_=DEFAULT_EISCP_IP;
125    else
126      receiverIP_=ip;
127    receiverPort_=DEFAULT_EISCP_PORT;
128  }
129
130
131  /** Constructor that takes your receivers ip and port,  gets all the class command
132   * constants set-up along with their command lookup maps (commandNameMap_ and commandMap_) .
133   **/
134  public Eiscp(String ip, int eiscpPort)
135  {
136    //initCommandMap();
137    if (ip==null || ip.equals(""))
138      receiverIP_=DEFAULT_EISCP_IP;
139    else
140      receiverIP_=ip;
141    if (eiscpPort<1 )
142      receiverPort_=DEFAULT_EISCP_PORT;
143    else
144      receiverPort_=eiscpPort;
145  }
146
147  /** Makes Chocolate glazed doughnuts. **/
148  public void setReceiverIP( String ip) { receiverIP_ = ip;}
149  /** Makes Sprinkle doughnuts. **/
150  public String getReceiverIP() {return receiverIP_;}
151  /** Makes mini doughnuts. **/
152  public void setReceiverPort( int port) { receiverPort_ = port;}
153  /** Makes glazed doughnuts. **/
154  public int getReceiverPort() {return receiverPort_;}
155
156  /**
157   * Connects to the receiver by opening a socket connection through the DEFaULT IP and DEFAULT eISCP port.
158   **/
159   public boolean connectSocket() { return connectSocket(null, -1);}
160
161
162  /**
163   * Connects to the receiver by opening a socket connection through the DEFAULT eISCP port.
164   **/
165   public boolean connectSocket(String ip) { return connectSocket(ip,-1);}
166
167
168  /**
169   * Connects to the receiver by opening a socket connection through the eISCP port.
170   **/
171  public boolean connectSocket(String ip, int eiscpPort)
172  {
173    if (ip==null || ip.equals("")) ip=receiverIP_;
174    if (eiscpPort<1 ) eiscpPort=receiverPort_;
175
176    if (eiscpSocket_==null || !connected_ || !eiscpSocket_.isConnected())
177    try
178    {
179      //1. creating a socket to connect to the server
180      eiscpSocket_ = new Socket(ip, eiscpPort);
181      System.out.println("Connected to "+ip+" on port "+eiscpPort);
182      //2. get Input and Output streams
183      out_ = new ObjectOutputStream(eiscpSocket_.getOutputStream());
184      in_ = new DataInputStream(eiscpSocket_.getInputStream());
185
186      //System.out.println("out_Init");
187      out_.flush();
188      // System.out.println("inInit");
189      connected_ = true;
190    }
191    catch(UnknownHostException unknownHost)
192    {
193      System.err.println("You are trying to connect to an unknown host!");
194    }
195    catch(IOException ioException)
196    {
197      System.err.println("Can't Connect: "+ioException.getMessage());
198    }
199    return connected_;
200  }
201
202
203  public boolean isConnected()
204  {
205    boolean retVal = true;
206    if (eiscpSocket_==null || !connected_ || !eiscpSocket_.isConnected())
207      retVal = false;
208    return retVal;
209  }
210
211
212  /**
213   * Tests the Connection to the receiver by opening a socket connection through the DEFaULT IP and DEFAULT eISCP port.
214   * @return true if already connected or can connect, and false if can't connect
215   **/
216   public boolean testConnection() { return testConnection(DEFAULT_EISCP_IP,DEFAULT_EISCP_PORT);}
217
218
219  /**
220   * Tests the Connection to the receiver by opening a socket connection through the specified IP and DEFAULT eISCP port.
221   * @param ip is the ip address (as a String) of the AV receiver to connect
222   * @return true if already connected or can connect, and false if can't connect
223   **/
224   public boolean testConnection(String ip) { return testConnection(DEFAULT_EISCP_IP,DEFAULT_EISCP_PORT);}
225
226
227  /**
228   * test the connection to the receiver by opening a socket connection through the eISCP port AND THEN CLOSES it if it was not already open.
229   * this method can be used when you need to specificly specify the IP and PORT. If the default port is used then you could also use the
230   * {@link #testConnection(String) testConnection} method (that used the default port) or the {@link #testConnection() testConnection}
231   * method (that used the default IP and port).
232   *
233   * @param ip is the ip address (as a String) of the AV receiver to connect
234   * @param eiscpPort is the IP Port of the AV receiver to connect with (Onkyo's default is 60128)
235   * @return true if already connected or can connect, and false if can't connect
236   **/
237  public boolean testConnection(String ip, int eiscpPort)
238  {
239    boolean retVal = false;
240    if (ip==null || ip.equals("")) ip=DEFAULT_EISCP_IP;
241    if (eiscpPort==0 ) eiscpPort=DEFAULT_EISCP_PORT;
242
243    if (connected_)
244    {
245      // test existing connection
246      if (eiscpSocket_.isConnected()) retVal = true;
247    }
248    else
249    {
250      // test a new connection
251      try
252      {
253        //1. creating a socket to connect to the server
254        eiscpSocket_ = new Socket(ip, eiscpPort);
255        if (eiscpSocket_!=null) eiscpSocket_.close();
256        retVal = true;
257      }
258      catch(UnknownHostException unknownHost)
259      {
260        System.err.println("You are trying to connect to an unknown host!");
261      }
262      catch(IOException ioException)
263      {
264        System.err.println("Can't Connect: "+ioException.getMessage());
265      }
266    }
267    return retVal;
268  }
269
270
271  /**
272   * Closes the socket connection.
273   * @return true if the closed succesfully
274   **/
275  public boolean closeSocket()
276  {
277    //4: Closing connection
278    try
279    {
280      boolean acted = false;
281      if (in_!=null) {in_.close();in_=null;acted = true;}
282      if (out_!=null) {out_.close();out_=null;acted = true;}
283      if (eiscpSocket_!=null) {eiscpSocket_.close();eiscpSocket_=null;acted = true;}
284      if (acted) System.out.println("closed connections");
285      connected_ = false;
286    }
287    catch(IOException ioException)
288    {
289      ioException.printStackTrace();
290    }
291    return connected_;
292  }
293
294
295  public static String convertAsciiToBase10(String str)  {return convertAsciiToBase10( str, false);}
296  public static String convertAsciiToBase10(String str,  boolean dumpOut)
297  {
298    char[] chars = str.toCharArray();
299    StringBuffer base10 = new StringBuffer();
300    if (dumpOut) System.out.print(" Base10: ");
301    for(int i = 0; i < chars.length; i++)
302    {
303      base10.append( (int)chars[i]);
304      if(i+1<chars.length) base10.append( ";" );
305      if (dumpOut) System.out.print("  "+((""+(int)chars[i]).length()==1?"0":"")+(int)chars[i] + " ");
306    }
307   if (dumpOut) System.out.println("");
308    return base10.toString();
309  }
310
311
312  /** Converts an ascii decimal String to a hex  String.
313   * @param str holding the string to convert to HEX
314   * @return a string holding the HEX representation of the passed in decimal str.
315   **/
316  public static String convertStringToHex(String str)
317  {
318     return convertStringToHex( str, false);
319  }
320
321
322  /** Converts an ascii decimal String to a hex  String.
323   * @param str holding the string to convert to HEX
324   * @param dumpOut flag to turn some debug output on/off
325   * @return a string holding the HEX representation of the passed in str.
326   **/
327  public static String convertStringToHex(String str,  boolean dumpOut)
328  {
329    char[] chars = str.toCharArray();
330    String out_put = "";
331
332    if (dumpOut) System.out.println("    Ascii: "+str);
333    if (dumpOut) System.out.print("    Hex: ");
334    StringBuffer hex = new StringBuffer();
335    for(int i = 0; i < chars.length; i++)
336    {
337      out_put = Integer.toHexString((int)chars[i]);
338      if (out_put.length()==1) hex.append("0");
339      hex.append(out_put);
340      if (dumpOut) System.out.print("0x"+(out_put.length()==1?"0":"")+ out_put+" ");
341    }
342    if (dumpOut) System.out.println("");
343    if (dumpOut) convertAsciiToBase10(str,dumpOut);
344    /*
345    if (dumpOut) System.out.print(" Base10: ");
346    for(int i = 0; i < chars.length; i++)
347    {
348      System.out.print("   "+convertHexNumberStringToDecimal(chars[i]));
349    }
350    if (dumpOut) System.out.println("");
351    */
352
353    return hex.toString();
354  }
355
356
357  /** Converts an HEX number String to its decimal equivalent.
358   * @param str holding the Hex Number string to convert to decimal
359   * @return an int holding the decimal equivalent of the passed in HEX numberStr.
360   **/
361  public static int convertHexNumberStringToDecimal(String str)
362  {
363    return convertHexNumberStringToDecimal(str, false);
364  }
365
366
367  /** Converts an HEX number String to its decimal equivalent.
368   * @param str holding the Hex Number string to convert to decimal
369   * @param dumpOut boolean flag to turn some debug output on/off
370   * @return an int holding the decimal equivalent of the passed in HEX numberStr.
371   **/
372  public static int convertHexNumberStringToDecimal(String str,  boolean dumpOut)
373  {
374    char[] chars = str.toCharArray();
375    String out_put = "";
376
377    if (dumpOut) System.out.println("\n      AsciiHex: 0x"+str);
378    if (dumpOut) System.out.print(  "       Decimal: ");
379
380    StringBuffer hex = new StringBuffer();
381    String hexInt = new String();
382    for(int i = 0; i < chars.length; i++)
383    {
384      out_put = Integer.toHexString((int)chars[i]);
385      if (out_put.length()==1) hex.append("0");
386      hex.append(out_put);
387      if (dumpOut) System.out.print((out_put.length()==1?"0":"")+ out_put);
388    }
389    hexInt = ""+(Integer.parseInt( hex.toString(), 16));
390    if (dumpOut) System.out.println("");
391    if (dumpOut) System.out.println( "      Decimal: "+hexInt.toString());
392
393    return Integer.parseInt(hexInt.toString());
394
395    //return Integer.decode("0x"+str);
396  }
397
398
399  /** Converts a hex byte to an ascii String.
400   * @param hex byte holding the HEX string to convert back to decimal
401   * @return a string holding the HEX representation of the passed in str.
402   **/
403  public static String convertHexToString(byte hex)
404  {
405    byte [] bytes = {hex};
406    return convertHexToString( new String(bytes), false);
407  }
408
409
410  /** Converts a hex String to an ascii String.
411   * @param hex the HEX string to convert back to decimal
412   * @return a string holding the HEX representation of the passed in str.
413   **/
414  public static String convertHexToString(String hex)
415  {
416    return convertHexToString( hex, false);
417  }
418
419
420  /** Converts a hex String to an ascii String.
421   * @param hex the HEX string to convert backk to decimal
422   * @param dumpOut boolean flag to turn some debug output on/off
423   * @return a string holding the HEX representation of the passed in str.
424   **/
425  public static String convertHexToString(String hex,  boolean dumpOut)
426  {
427
428    StringBuilder sb = new StringBuilder();
429    StringBuilder temp = new StringBuilder();
430    String out_put = "";
431
432    if (dumpOut) System.out.print("    Hex: ");
433    //49204c6f7665204a617661 split into two characters 49, 20, 4c...
434    for( int i=0; i<hex.length()-1; i+=2 ){
435
436        //grab the hex in pairs
437        out_put = hex.substring(i, (i + 2));
438        if (dumpOut) System.out.print("0x"+out_put+" ");
439        //convert hex to decimal
440        int decimal = Integer.parseInt(out_put, 16);
441        //convert the decimal to character
442        sb.append((char)decimal);
443
444        temp.append(decimal);
445    }
446    if (dumpOut) System.out.println("    Decimal : " + temp.toString());
447
448    return sb.toString();
449  }
450
451
452  /**
453    * Wraps a command in a eiscp data message (data characters).
454    *
455    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
456    * @return StringBuffer holing the full iscp message packet , empty if not  a valid command or parameter
457    **/
458  public StringBuilder getEiscpMessage(int command){ return getEiscpMessage(command, null);}
459  public StringBuilder getEiscpMessage(int command, String param)
460  {
461    StringBuilder sb = new StringBuilder("");
462    String cmdStr = "";
463
464    if(command==iscp_.VOLUME_SET || command==iscp_.TUNING_FREQ)
465    {
466      if(param==null || param.trim().equals(""))
467      {
468        param = "";  // NOT Valid so leave sb empty so it does NOT get sent
469      }
470      else
471        if(param.trim().length() == 2 || param.trim().length() == 5)
472        {
473          try
474          {
475            // test if valid
476            int p = Integer.parseInt(param.trim());
477
478            //if no Exception, then good to go... stitch it together
479            if (command==iscp_.VOLUME_SET) cmdStr = getVolumeCmdStr(p);
480            else if (command==iscp_.TUNING_FREQ) cmdStr = getTuneFreqCmdStr(param.trim());
481          }
482          catch(java.lang.NumberFormatException  nfEx)
483          {
484            param = "";
485            // NOT Valid so leave sb empty so it does NOT get sent
486          }
487        }
488        else
489          param = "";// NOT Valid so leave sb empty so it does NOT get sent
490    }
491    else
492      cmdStr = iscp_.getCommandStr(command);
493
494    if(!cmdStr.equals(""))
495    {
496      int eiscpDataSize = iscp_.getCommandStr(command).length() + 2 ; // this is the eISCP data size
497      int eiscpMsgSize = eiscpDataSize + 1 + 16 ; // this is the eISCP data size
498
499      /* This is where I construct the entire message
500          character by character. Each char is represented by a 2 disgit hex value */
501      sb.append("ISCP");
502      // the following are all in HEX representing one char
503
504      // 4 char Big Endian Header
505      sb.append((char)Integer.parseInt("00", 16));
506      sb.append((char)Integer.parseInt("00", 16));
507      sb.append((char)Integer.parseInt("00", 16));
508      sb.append((char)Integer.parseInt("10", 16));
509
510      // 4 char  Big Endian data size
511      sb.append((char)Integer.parseInt("00", 16));
512      sb.append((char)Integer.parseInt("00", 16));
513      sb.append((char)Integer.parseInt("00", 16));
514      // the official ISCP docs say this is supposed to be just the data size  (eiscpDataSize)
515      // ** BUT **
516      // It only works if you send the size of the entire Message size (eiscpMsgSize)
517      sb.append((char)Integer.parseInt(Integer.toHexString(eiscpMsgSize), 16));
518
519      // eiscp_version = "01";
520      sb.append((char)Integer.parseInt("01", 16));
521
522      // 3 chars reserved = "00"+"00"+"00";
523      sb.append((char)Integer.parseInt("00", 16));
524      sb.append((char)Integer.parseInt("00", 16));
525      sb.append((char)Integer.parseInt("00", 16));
526
527      //  eISCP data
528      // Start Character
529      sb.append("!");
530
531      // eISCP data - unittype char '1' is receiver
532      sb.append("1");
533
534      // eISCP data - 3 char command and param    ie PWR01
535      sb.append(cmdStr);
536
537      // msg end - EOF
538      sb.append((char)Integer.parseInt("0D", 16));
539
540      //System.out.println("  eISCP data size: "+eiscpDataSize +"(0x"+Integer.toHexString(eiscpDataSize) +") chars");
541      //System.out.println("  eISCP msg size: "+sb.length() +"(0x"+Integer.toHexString(sb.length()) +") chars");
542    }
543
544    return sb;
545  }  //getEiscpMessage
546
547
548  /** dumps all the commands to System.out along with its associated eIscp message string in BASE10 numbers.
549    * For example:< br />LISTEN_MODE_THEATER_DIMENSIONAL:
550    **/
551  public void dumpBinaryMessages()
552  {
553    StringBuilder sb = null;
554    Iterator<String> it =iscp_.getIterator();
555    String currCommandName = null;
556    while( it.hasNext())
557    {
558      currCommandName = it.next();
559      sb = getEiscpMessage(iscp_.getCommand(currCommandName));
560      System.out.println(currCommandName+": "+convertAsciiToBase10(sb.toString()));
561    }
562  }
563
564
565  /**
566    * Sends to command ultiple times sequentially to the receiver and WAITS until each is completed.
567    *
568    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
569    * @param numRepeats how many doughnuts to buy.
570    * @param closeSocketWhenDone do we hold the door open for the next doughnut customer , after we leave
571    **/
572  public void sendCommandMultipleTimes(int command, int numRepeats, boolean closeSocketWhenDone)
573  {
574    for(int i=0;i<numRepeats;i++)
575    {
576      sendQueryCommand(command, false);
577      sleep(50); // EISCP specifies a delay
578    }
579    if (closeSocketWhenDone) closeSocket();
580  }
581
582
583  /**
584    * Sends to command ultiple times sequentially to the receiver and WAITS until each is completed and
585    * hold the door open for the next doughnut customer , after we leave.
586    *
587    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
588    * @param numRepeats how many doughnuts to buy.
589    **/
590  public void sendCommandMultipleTimes(int command, int numRepeats)
591  {
592    sendCommandMultipleTimes( command,  numRepeats, false);
593  }
594
595
596  /**
597    * Sends to command to the receiver and does not wait for a reply.
598    *
599    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
600    **/
601  public void sendCommand(int command)
602  {
603    sendCommand(command, "", false);
604  }
605
606
607  /**
608    * Sends to command to the receiver and does not wait for a reply.
609    *
610    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
611    **/
612  public void sendCommand(int command, String param)
613  {
614    sendCommand(command, param, false);
615  }
616
617
618  /**
619    * Sends to command to the receiver and does not wait for a reply.
620    *
621    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
622    * @param param paramater to customize a command Str for VOLUME_SET ?? or TUNING_FREQ ?????
623    * @param closeSocket flag to close the connection when done or leave it open.
624    **/
625  public void sendCommand(int command, String param, boolean closeSocket)
626  {
627    StringBuilder sb = new StringBuilder("");
628
629    if(command==iscp_.VOLUME_SET || command==iscp_.TUNING_FREQ)
630    {
631      if(param==null || param.trim().equals(""))
632      {
633        param = "";  // NOT Valid so leave sb empty so it does NOT get sent
634      }
635      else
636        if(param.trim().length() == 2 || param.trim().length() == 5)
637        {
638          try
639          {
640            // test if valid
641            int p = Integer.parseInt(param.trim());
642
643            //if no Exception, then good to go... stitch it together
644            sb = getEiscpMessage(command, param.trim());
645            //sb.append(param.trim());
646          }
647          catch(java.lang.NumberFormatException  nfEx)
648          {
649            param = "";
650            // NOT Valid so leave sb empty so it does NOT get sent
651          }
652        }
653        else
654          param = "";// NOT Valid so leave sb empty so it does NOT get sent
655    }
656    else
657      sb = getEiscpMessage(command);
658
659    if(sb.toString().trim().length()>0 && connectSocket())
660    {
661      try
662      {
663        System.out.println("  sending "+sb.length() +" chars: ");
664        convertStringToHex(sb.toString(), true);
665        //out_.writeObject(sb.toString());
666        //out_.writeChars(sb.toString());
667        out_.writeBytes(sb.toString());  // <--- This is the one that works
668        //out_.writeBytes(convertStringToHex(sb.toString(), false));
669        //out_.writeChars(convertStringToHex(sb.toString(), false));
670        out_.flush();
671        System.out.println("sent!" );
672      }
673      catch(java.net.SocketException socketException)
674      {
675        System.out.println(" Socket Was Disconnected... Reconnecting and Resending");
676        connectSocket();
677        try
678        {
679          out_.writeBytes(sb.toString());
680          out_.flush();
681          System.out.println("sent!" );
682        }
683        catch(IOException ioEx)
684        {
685          ioEx.printStackTrace();
686        }
687      }
688      catch(IOException ioException)
689      {
690        ioException.printStackTrace();
691      }
692    }
693    if (closeSocket) closeSocket();
694  }
695
696
697  /**
698    * Sends to command to the receiver and then waits for the response(s) <br />and returns only the response packetMessages related to the command requested<br />and closes the socket.
699    * if you want to see ALL the responses use the sendQueryCommand(int command, boolean closeSocket, boolean returnAll) method.
700    *
701    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
702    * @return the response to the command
703    **/
704  public String sendQueryCommand(int command)
705  {
706    return sendQueryCommand( command,  false);
707  }
708
709
710  /**
711    * Sends to command to the receiver and then waits for the response(s) <br />and returns only the response packetMessages related to the command requested.
712    * if you want to see ALL the responses use the sendQueryCommand(int command, boolean closeSocket, boolean returnAll) method.
713    *
714    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
715    * @param closeSocket flag to close the connection when done or leave it open.
716    * @return the response to the command
717    **/
718  public String sendQueryCommand(int command, boolean closeSocket)
719  {
720    return sendQueryCommand( command,  closeSocket, false);
721  }
722
723
724  /**
725    * Sends to command to the receiver and then waits for the response(s). The responses often have nothing to do with the command sent
726    * so this method can filter them to return only the responses related to the command sent.
727    *
728    * @param command must be one of the Command Class Constants from the eiscp.Eiscp.Command class.
729    * @param closeSocket flag to close the connection when done or leave it open.
730    * @param returnAll flags if all response packetMessages are returned, if no then ONLY the ones related to the command requested
731    * @return the response to the command
732    **/
733  public String sendQueryCommand(int command, boolean closeSocket, boolean returnAll)
734  {
735    String retVal = "";
736
737    /* Send The Command and then... */
738    System.out.println("   sendQueryCommand ("+command+", "+closeSocket+", "+returnAll+")");
739    System.out.println("                     command="+iscp_.getCommandStr(command));
740    sendCommand(command,null,false);
741    sleep(50); // docs say so
742
743    /* now listen for the response. */
744    Vector <String> rv = null;
745    if(returnAll)
746      rv = readQueryResponses();
747    else
748      rv = readQueryResponses(command);
749    String currResponse = "";
750    for (int i=0; i < rv.size(); i++)
751    {
752      currResponse = (String) rv.elementAt(i);
753      /* Send ALL responses OR just the one related to the commad sent??? */
754      if (returnAll || currResponse.startsWith(iscp_.getCommandStr(command).substring(0,3)))
755        retVal+= currResponse+"\n";
756    }
757
758    if (closeSocket) closeSocket();
759
760    return retVal ;
761  }
762
763
764  /**
765   * This method reads ALL responses (possibly more than one) after a query command.
766   * @return an array of the data portion of the response messages only - There might be more than one response message received.
767   **/
768  public Vector <String> readQueryResponses() { return readQueryResponses(-1);}
769  /**
770   * This method reads responses (possibly more than one) after a query command. It can end
771   * early when it finds the respose you are waitng for by passing in the command you called.
772   * @param command is used to end the response processing early when it finds the command - if you want all responses processed pass in -1
773   * @return an array of the data portion of the response messages only - There might be more than one response message received.
774   **/
775  public Vector <String> readQueryResponses(int command)
776  {
777    //boolean debugging = debugging_;
778    boolean foundCommand = false;
779    Vector <String> retVal = new Vector <String> ();
780    byte [] responseBytes = new byte[32] ;
781    String currResponse = "";
782    int numBytesReceived = 0;
783    int totBytesReceived = 0;
784    int i=0;
785    String dataMsgStr = "";
786    int packetCounter=0;
787    int headerSizeDecimal = 0;
788    int dataSizeDecimal = 0;
789    char endChar1 ='!';// NR-5008 response sends 3 chars to terminate the packet - 0x1a 0x0d 0x0a
790    char endChar2 ='!';
791    char endChar3 ='!';
792    int readDelay = 0;
793
794    if(connected_)
795    {
796      try
797      {
798        System.out.println("\nReading Response Packet,looking for "+(command>0?iscp_.getCommandStr(command):"ALL responses"));
799        eiscpSocket_.setSoTimeout(socketTimeOut_); // this must be set or the following read will BLOCK / hang the method when the messages are done
800
801        //readDelay = calendar_.get(calendar_.MILLISECOND);
802        while(!foundCommand && ((numBytesReceived = in_.read(responseBytes))>=0) )
803        {
804          totBytesReceived = 0;
805          StringBuilder msgBuffer = new StringBuilder("");
806          if (debugging_) System.out.println("\n*\n*\n*\n*Buffering received bytes: "+numBytesReceived);
807          if (debugging_) System.out.print( " Packet"+"["+packetCounter+"]:");
808
809          /* Read ALL the incoming Bytes and buffer them */
810          // *******************************************
811          while(numBytesReceived>0 )
812          {
813            totBytesReceived+=numBytesReceived;
814            msgBuffer.append(new String(responseBytes));
815            responseBytes = new byte[32];
816            numBytesReceived = 0;
817            if (in_.available()>0)
818              numBytesReceived = in_.read(responseBytes);
819            if (debugging_) System.out.print(" "+numBytesReceived);
820          }
821          if (debugging_) System.out.println();
822          convertStringToHex(msgBuffer.toString(), debugging_);
823
824          /* Response is done... process it into dataMessages */
825          // *******************************************
826          char [] responseChars = msgBuffer.toString().toCharArray(); // use the charArray to step through
827          msgBuffer = null;// clear for garbageCollection
828
829          System.out.println("\n   "+i+") responseChars.length="+responseChars.length);
830          int responseByteCnt = 0;
831          char versionChar = '1';
832          char dataStartChar = '!';
833          char dataUnitChar = '1';
834          char [] headerSizeBytes = new char[4];
835          char [] dataSizeBytes  = new char[4];
836          char [] dataMessage = null ; //init dynamically
837          int dataByteCnt = 0;
838
839          // loop through all the chars and split out the dataMessages
840          while (!foundCommand && (responseByteCnt< totBytesReceived))
841          {
842            /* read Header */
843            // 1st 4 chars are the leadIn
844            responseByteCnt+=4;
845
846            // read headerSize
847            headerSizeBytes[0]=responseChars[responseByteCnt++];
848            headerSizeBytes[1]=responseChars[responseByteCnt++];
849            headerSizeBytes[2]=responseChars[responseByteCnt++];
850            headerSizeBytes[3]=responseChars[responseByteCnt++];
851
852            // 4 char Big Endian data size;
853            dataSizeBytes[0]=responseChars[responseByteCnt++];
854            dataSizeBytes[1]=responseChars[responseByteCnt++];
855            dataSizeBytes[2]=responseChars[responseByteCnt++];
856            dataSizeBytes[3]=responseChars[responseByteCnt++];
857
858            if (debugging_) System.out.println(" -HeaderSize-");
859            headerSizeDecimal = convertHexNumberStringToDecimal(new String(headerSizeBytes), debugging_);
860            if (debugging_) System.out.println(" -DataSize-");
861            dataSizeDecimal = convertHexNumberStringToDecimal(new String(dataSizeBytes), debugging_);
862
863            // version
864            versionChar = responseChars[responseByteCnt++];
865
866            // 3 reserved bytes
867            responseByteCnt+=3;
868            dataByteCnt = 0;
869
870            // Now the data message
871            dataStartChar = responseChars[responseByteCnt++]; // parse and throw away (like parsley)
872            dataUnitChar = responseChars[responseByteCnt++]; // dito
873            dataMessage = null;
874            if (debugging_) System.out.println("new dataMessage["+dataSizeDecimal+"]");
875            if(dataSizeDecimal<4096)
876              dataMessage = new char [dataSizeDecimal];
877            else
878            {
879              dataMessage= new char[0];
880              System.out.println("error data message size hexVal="+dataSizeBytes);
881              break;
882            }
883
884            /* Get the dataMessage from this response */
885            // NR-5008 response sends 3 chars to terminate the packet - so DON't include them in the message
886            while( dataByteCnt < (dataSizeDecimal-5) && responseByteCnt< (totBytesReceived-3))
887            {
888              dataMessage[dataByteCnt++] = responseChars[responseByteCnt++];
889            }
890            dataMsgStr = new String(dataMessage);
891            if (debugging_) System.out.println("dataMessage:\n~~~~~~~~~~~~~");
892            if (debugging_) System.out.println("              "+dataMsgStr);
893            retVal.addElement(dataMsgStr);
894
895            // Read the end packet char(s) "[EOF]"
896            // [EOF]                    End of File             ASCII Code 0x1A
897            // NOTE: the end of packet char (0x1A) for a response message is DIFFERENT that the sent message
898            // NOTE: ITs also different than what is in the Onkyo eISCP docs
899            // NR-5008 sends 3 chars to terminate the packet - 0x1a 0x0d 0x0a
900            endChar1 = responseChars[responseByteCnt++];
901            endChar2 = responseChars[responseByteCnt++];
902            endChar3 = responseChars[responseByteCnt++];
903            if (endChar1 == (char)Integer.parseInt("1A", 16) &&
904                endChar2 == (char)Integer.parseInt("0D", 16) &&
905                endChar3 == (char)Integer.parseInt("0A", 16)
906               )
907            if (debugging_) System.out.println(" EndOfPacket["+packetCounter+"]\n");
908            packetCounter++;
909            // Now check if we end early
910            if (command!=-1 && dataMsgStr.startsWith(iscp_.getCommandStr(command).substring(0,3)))
911            {
912              foundCommand = true;
913              System.out.println("       Found Matched Response:"+iscp_.getCommandStr(command) +" = " + dataMsgStr);
914            }
915          }// done packet
916          System.out.println("       Found Response:"+iscp_.getCommandStr(command) +" = " + dataMsgStr);
917          i++;
918          readDelay = 0;
919        } // check for more data
920
921      }
922      catch( java.net.SocketTimeoutException  noMoreDataException)
923      {
924        System.out.println(" EXCEPTION SocketTimeout after "+i+" packets already found");
925        System.out.println("          received: \""+retVal+"\"" );
926        if(i>0) retVal.addElement(dataMsgStr);
927        noMoreDataException.printStackTrace();
928      }
929      catch(EOFException  eofException)
930      {
931        System.out.println("received: \""+retVal+"\"" );
932      }
933      catch(IOException ioException)
934      {
935        ioException.printStackTrace();
936      }
937    }
938    else
939      System.out.println("!!Not Connected to Receive ");
940    return retVal;
941  }
942
943
944  /** This method creates the set volume command based on the passed value. **/
945  public static  String getVolumeCmdStr(){return iscp_.getVolumeCmdStr(volume_);}
946  public static  String getVolumeCmdStr(int vol){return iscp_.getVolumeCmdStr(vol);}
947
948
949  /** This method creates the set volume command based on the passed value. **/
950  public static  String getTuneFreqCmdStr(String freqStr){return iscp_.getTuneFreqCmdStr(freqStr);}
951
952
953  /** Converts the VOLUME_QUERY response into ascii decimal result. **/
954  public static  String decipherVolumeResponse(String queryResponses)
955  {
956    String [] responses = queryResponses.split("[\n]");
957    String retVal = "VOLUME_QUERY response: ";
958    String queryResponse = "";
959    String volVal = "00";
960    Integer IntVal = Integer.decode("0x"+volVal);
961    for (int i=0; i< responses.length; i++)
962    {
963      queryResponse = responses[i];
964      if (queryResponse.startsWith(iscp_.getCommandStr(iscp_.VOLUME_SET)))
965      {
966        try
967        {
968          volVal = queryResponse.trim().substring(3);
969          IntVal = Integer.decode("0x"+volVal);
970          retVal +="\n   Volume=" + IntVal;
971          retVal +=" (0x" + volVal +")\n";
972        }
973        catch (Exception ex)
974        {
975          // Ignore for now
976        }
977    }
978    }
979    return retVal;
980  }
981
982
983  /** Converts the SOURCE_QUERY response into ascii string result. **/
984  public static  String deciherSourceResponseToString(String queryResponses)
985  {
986    String [] responses = queryResponses.split("[\n]");
987    String queryResponse = "";
988    String sourceCodeStr = "00";
989    String sourceString = "00";
990    for (int i=0; i< responses.length; i++)
991    {
992      queryResponse = responses[i].trim();
993      if (queryResponse.startsWith("response:"))
994      {
995        sourceCodeStr = queryResponse.substring("response:".length()).trim();
996        sourceString = iscp_.getCommandStr(
997                            iscp_.getCommandKey(sourceCodeStr.substring(1,sourceCodeStr.length()-1))
998                       );
999      }
1000    }
1001    return iscp_.getCommandName(queryResponses); //sourceString;
1002  }
1003
1004
1005  /** Converts the VOLUME_QUERY response into ascii decimal result. **/
1006  public static  String deciherVolumeResponseToDecimal(String queryResponses)
1007  {
1008    String [] responses = queryResponses.split("[\n]");
1009    String retVal = "";
1010    String queryResponse = "";
1011    String volVal = "00";
1012    Integer IntVal = Integer.decode("0x"+volVal);
1013    for (int i=0; i< responses.length; i++)
1014    {
1015      queryResponse = responses[i];
1016      if (queryResponse.startsWith(iscp_.getCommandStr(iscp_.VOLUME_SET)))
1017      {
1018        try
1019        {
1020          volVal = queryResponse.trim().substring(3);
1021          IntVal = Integer.decode("0x"+volVal);
1022          retVal ="" + IntVal;
1023    //      retVal ="0x" + volVal ;
1024        }
1025        catch (Exception ex)
1026        {
1027          // Ignore for now
1028        }
1029      }
1030    }
1031    return retVal;
1032  }
1033
1034
1035  /** Converts the VOLUME_QUERY response into ascii hex result. **/
1036  public static  String deciherVolumeResponseToHex(String queryResponses)
1037  {
1038    String [] responses = queryResponses.split("[\n]");
1039    String retVal = "";
1040    String queryResponse = "";
1041    String volVal = "00";
1042    Integer IntVal = Integer.decode("0x"+volVal);
1043    for (int i=0; i< responses.length; i++)
1044    {
1045      queryResponse = responses[i];
1046      if (queryResponse.startsWith(iscp_.getCommandStr(iscp_.VOLUME_SET)))
1047      {
1048        try
1049        {
1050          volVal = queryResponse.trim().substring(3);
1051          IntVal = Integer.decode("0x"+volVal);
1052    //      retVal ="" + IntVal;
1053          retVal ="0x" + volVal ;
1054        }
1055        catch (Exception ex)
1056        {
1057          // Ignore for now
1058        }
1059      }
1060    }
1061    return retVal;
1062  }
1063
1064
1065  /** This method takes the  3 character response from the USB Play status query (NETUSB_PLAY_STATUS_QUERY) and creates a human readable String.
1066   * NET/USB Play Status QUERY returns one of 3 letters - PRS.<oL>
1067   * <LI>p -> Play Status<ul><li>"S": STOP</li><li>"P": Play</li><li>"p": Pause</li><li>"F": FF</li><li>"R": FastREW</li></ul></LI>
1068   * <LI>r -> Repeat Status<ul><li>"-": Off</li><li>"R": All</li><li>"F": Folder</li><li>"1": Repeat 1</li></ul></LI>
1069   * <LI>s -> Shuffle Status<ul><li>"-": Off</li><li>"S": All</li><li>"A": Album</li><li>"F": Folder</li></ul></LI></oL>
1070   * @param queryResponses is the entire response packet with the oneOf3 char reply embedded in it.
1071  **/
1072  public   String decipherUsbPlayStatusResponse(String queryResponses)
1073  {
1074    String [] responses = queryResponses.split("[\n]");
1075    String retVal = "NETUSB_PLAY_STATUS_QUERY response: "+ queryResponses.trim();
1076    String queryResponse = "";
1077    for (int i=0; i< responses.length; i++)
1078    {
1079      queryResponse = responses[i];
1080      if (queryResponse.substring(3,4).equals("P") )
1081      {
1082        retVal += "\n  Play Status: ";
1083        if (queryResponse.substring(5).equals("S") )
1084          retVal +="Stop";
1085        else if (queryResponse.substring(5).equals("P") )
1086          retVal +="Play";
1087        else if (queryResponse.substring(5).equals("p") )
1088          retVal +="Pause";
1089        else if (queryResponse.substring(5).equals("F") )
1090          retVal +="FastForward";
1091        else if (queryResponse.substring(5).equals("R") )
1092          retVal +="FastRewind";
1093        else retVal+= "NotSpecified";
1094      }
1095
1096      if (queryResponse.substring(3,4).equals("R") )
1097      {
1098        retVal += "\n  Repeat Status: ";
1099        if (queryResponse.substring(5).equals("-") )
1100          retVal +="Off";
1101        else if (queryResponse.substring(5).equals("R") )
1102          retVal +="All";
1103        else if (queryResponse.substring(5).equals("F") )
1104          retVal +="Folder";
1105        else if (queryResponse.substring(5).equals("1") )
1106          retVal +="1 song";
1107        else retVal+= "NotSpecified";
1108      }
1109
1110      if (queryResponse.substring(3,4).equals("S") )
1111      {
1112        retVal += "\n  Schuffle Status: ";
1113        if (queryResponse.trim().substring(5).equals("-") )
1114          retVal +="Off";
1115        else if (queryResponse.trim().substring(5).equals("S") )
1116          retVal +="All";
1117        else if (queryResponse.trim().substring(5).equals("A") )
1118          retVal +="Album";
1119        else if (queryResponse.trim().substring(5).equals("F") )
1120          retVal +="Folder";
1121        else retVal+= "NotSpecified";
1122      }
1123    }
1124
1125    return retVal;
1126  }
1127
1128
1129  /** Converts the response packets into a JSON result. **/
1130  public   String decipherNetUsbResponse(int command, String queryResponses)
1131  {
1132    String [] responses = queryResponses.split("[\n]");
1133
1134    System.out.println("decipherNetUsbResponse responses= "+java.util.Arrays.toString(responses));
1135    String retVal = "{\n         \"command\": \""+ iscp_.getCommandStr(command)+"\"";
1136    retVal += ",\n          \"numberOfPackets\": "+responses.length;
1137    retVal += ",\n          \"responsePackets\": [";
1138    String queryResponse = "";
1139    boolean debugging = true;
1140      String [] dirEntry = new String[responses.length];
1141      String [] parentDirEntry = new String[responses.length];
1142
1143    System.out.println("\n  Eiscp Processing command="+iscp_.getCommandStr(command));
1144    if (command==iscp_.NETUSB_SONG_TRACK_QUERY)
1145    {
1146      System.out.println("\n  Eiscp Processing command="+iscp_.getCommandStr(command));
1147      for (int i=0; i< responses.length; i++)
1148      {
1149        queryResponse=responses[i].trim();
1150        //System.out.println("\n        "+queryResponse);
1151        if(queryResponse.trim().startsWith("NLTF") ) retVal+= "\nDir ="+queryResponse;
1152        if(queryResponse.trim().startsWith("NLSU") ) retVal+= "\nSong "+queryResponse.substring(4,6).trim()+" = "+queryResponse.substring(6).trim();
1153        //if(i>0) retVal += ",";
1154        //retVal += "\n                   \""+queryResponse+"\"";
1155
1156      }
1157    }
1158    else if (command==iscp_.NETUSB_OP_SELECT || command==iscp_.NETUSB_OP_RETURN|| command==iscp_.NETUSB_OP_DISPLAY)
1159    {
1160      int selectedEntry= -1;
1161      //  DISPLAY uses NLSC?P as the slected entry indicator
1162      for (int i=0; i< responses.length && selectedEntry>-1; i++)
1163      {
1164        queryResponse=responses[i].trim();
1165        if(queryResponse!=null && queryResponse.length()>4 && queryResponse.indexOf("NLSC")>-1 )
1166          selectedEntry=Integer.parseInt(queryResponse.substring(4,5)) ;
1167      }
1168
1169      String selectedPacketKey = "NLSU"+selectedEntry+"-";
1170      String currentEntry = "";
1171      String parentEntry = "";
1172      int entryCounter = 0;
1173      int parentEntryCounter = 0;
1174      boolean inCurrDir = false;
1175      int dirLevel = -1;
1176
1177      for (int i=0; i< responses.length; i++)
1178      {
1179        queryResponse=responses[i].trim();
1180        if(queryResponse!=null && queryResponse.length()>6 && queryResponse.indexOf("NLTF10")>-1 && Integer.parseInt(queryResponse.substring( 6,7)) > dirLevel) //NLTF10200000000000002FF00SND
1181        {
1182          if( debugging ) System.out.println("  DEBUG: deciphering "+"NETUSB_OP_SELECT"+" currDirLevel="+dirLevel+"   parsed val="+Integer.parseInt(queryResponse.substring( 6,7)));
1183          dirLevel = Integer.parseInt(queryResponse.substring( 6,7));
1184        }
1185      }
1186      if( debugging ) System.out.println("  DEBUG: current dir is listed Under: NLTF10"+dirLevel);
1187
1188      for (int i=0; i< responses.length; i++)
1189      {
1190        queryResponse=responses[i].trim();
1191
1192        if(queryResponse.indexOf(selectedPacketKey)>-1)
1193        {
1194          //currentEntry = queryResponse.substring( queryResponse.indexOf("02FF0")+"02FF0".length()+1 ).trim();
1195          currentEntry = queryResponse.substring( queryResponse.indexOf(selectedPacketKey)+selectedPacketKey.length() ).trim();
1196          inCurrDir=true;
1197        }
1198
1199        if(queryResponse.indexOf("NLTF10")>-1 && queryResponse.indexOf("02FF0") >-1)
1200        {
1201          parentEntry  = queryResponse.substring( queryResponse.indexOf("02FF0")+"02FF0".length()+1 ).trim();
1202          inCurrDir=false;
1203        }
1204
1205        if(queryResponse.indexOf("NLSU")>-1 )
1206          dirEntry[entryCounter++] = queryResponse.substring( queryResponse.indexOf("NLSU")+"NLSU".length()+2 ).trim();
1207        //else if(queryResponse.indexOf("NLSU")>-1)
1208        //  parentDirEntry[parentEntryCounter++] = queryResponse.substring( queryResponse.indexOf("NLSU")+"NLSU".length()+2 ).trim();
1209
1210        if( debugging && queryResponse!=null && !"".equals(queryResponse))
1211        {
1212          if(i==0)  retVal += "\n";
1213          else     retVal += ",\n";
1214          retVal += "                   \""+queryResponse+"\"";
1215        }
1216      }
1217      //if( debugging ) retVal += ",";
1218      retVal += "\n                 ]";
1219      retVal += ",\n         \"currentDir\": \""+currentEntry+"\"";
1220
1221      if(entryCounter>0)
1222      {
1223        retVal += ",\n        \"currentDirEntries\": [ \n";
1224        for (int i=0; i< entryCounter; i++)
1225        {
1226          if (i>0) {retVal += ",";}
1227          //retVal += "\n                       \"entry"+i+"\": ";
1228          retVal += "\""+dirEntry[i]+"\"";
1229        }
1230        retVal += "\n              ] ";
1231      }
1232
1233      retVal += ",\n        \"parentEntry\": \""+parentEntry+"\"";
1234      if(parentEntryCounter>0)
1235      {
1236        retVal += ",\n        \"parentDirEntries\": [ \n";
1237        for (int i=0; i< parentEntryCounter; i++)
1238        {
1239          if (i>0) {retVal += ",";}
1240          //retVal += "\n                       \"entry"+i+"\": ";
1241          retVal += "\""+parentDirEntry[i]+"\"";
1242        }
1243        retVal += "\n              ] ";
1244      }
1245    }
1246    else if (command==iscp_.NETUSB_OP_DOWN || command==iscp_.NETUSB_OP_UP)
1247    {
1248      //  DISPLAY uses NLSU?P as the slected entry indicator/KEY
1249      int selectedEntry= Integer.parseInt(responses[responses.length-1].substring(4,5)) ;
1250      String selectedPacketKey = "NLSU"+selectedEntry+"-";
1251      String currentEntry = "";
1252      String parentEntry = "";
1253      for (int i=0; i< responses.length; i++)
1254      {
1255        queryResponse=responses[i].trim();
1256
1257        // NLSU0 is the currentselection when NLSC0C is the last packet
1258        // NLSU1 is the currentselection when NLSC1C is the last packet
1259        if(queryResponse.indexOf(selectedPacketKey)>-1) currentEntry = queryResponse.substring( queryResponse.indexOf(selectedPacketKey)+selectedPacketKey.length() ).trim();
1260        if(queryResponse.indexOf("2FF00")>-1)  parentEntry  = queryResponse.substring( queryResponse.indexOf("2FF00")+"2FF00".length() ).trim();
1261        if( debugging && queryResponse!=null && !"".equals(queryResponse))
1262        {
1263          if(i==0)  retVal += "\n";
1264          else     retVal += ",\n";
1265          retVal += "                  \""+queryResponse+"\"";
1266        }
1267      }
1268      retVal += "\n                 ]";
1269      retVal += ",\n                  \"currentDir\": \""+currentEntry+"\"";;
1270      retVal += ",\n                  \"parentEntry\": \""+parentEntry+"\"";;
1271
1272    }
1273    else if (command==iscp_.NETUSB_OP_RIGHT || command==iscp_.NETUSB_OP_LEFT)
1274    {
1275      String currentEntry = "";
1276      String parentEntry = "";
1277      for (int i=0; i< responses.length; i++)
1278      {
1279        queryResponse=responses[i].trim();
1280
1281        if(queryResponse.indexOf("NLSU0-")>-1) currentEntry = queryResponse.substring( queryResponse.indexOf("NLSU0-")+"NLSU0-".length() ).trim();
1282        if(queryResponse.indexOf("NLTF101")>-1) parentEntry = queryResponse.substring( queryResponse.indexOf("2FF00")+"2FF00".length() ).trim();
1283        if( debugging && queryResponse!=null && !"".equals(queryResponse))
1284        {
1285          if(i==0)  retVal += "\n";
1286          else     retVal += ",\n";
1287          retVal += "                   \""+queryResponse+"\"";
1288        }
1289      }
1290      retVal += "\n                 ]";
1291      retVal += ",\n                  \"currentDir\": \""+currentEntry+"\"";;
1292      retVal += ",\n                  \"parentEntry\": \""+parentEntry+"\"";;
1293    }
1294    else if (command==iscp_.NETUSB_OP_CHANUP || command==iscp_.NETUSB_OP_CHANDWN)
1295    {
1296      System.out.println("Processing command="+iscp_.getCommandStr(command));
1297      int selectedEntry= -1;
1298      //  DISPLAY uses NLSC?P as the slected entry indicator
1299      for (int i=0; i< responses.length; i++)
1300      {
1301        queryResponse=responses[i].trim();
1302        if(queryResponse.indexOf("NLSC")>-1 ) selectedEntry=Integer.parseInt(queryResponse.substring(4,5)) ;
1303      }
1304
1305      String selectedPacketKey = "NLSU"+selectedEntry+"-";
1306      String currentEntry = "";
1307      String parentEntry = "";
1308      for (int i=0; i< responses.length; i++)
1309      {
1310        queryResponse=responses[i].trim();
1311
1312        // NLSU0 is the currentselection when NLSC0C is the last packet
1313        // NLSU1 is the currentselection when NLSC1C is the last packet
1314        if(queryResponse.indexOf(selectedPacketKey)>-1) currentEntry = queryResponse.substring( queryResponse.indexOf(selectedPacketKey)+selectedPacketKey.length() ).trim();
1315        if(queryResponse.indexOf("2FF00")>-1)  parentEntry  = queryResponse.substring( queryResponse.indexOf("2FF00")+"2FF00".length() ).trim();
1316        if( debugging && queryResponse!=null && !"".equals(queryResponse))
1317        {
1318          if(i==0)  retVal += "\n";
1319          else     retVal += ",\n";
1320          retVal += "                  \""+queryResponse+"\"";
1321        }
1322      }
1323      retVal += "\n                 ]";
1324      retVal += ",\n                  \"currentDir\": \""+currentEntry+"\"";;
1325      retVal += ",\n                  \"parentEntry\": \""+parentEntry+"\"";;
1326
1327    }
1328    else if (command==iscp_.NETUSB_PLAY_STATUS_QUERY)
1329    {
1330      System.out.println("Processing command="+iscp_.getCommandStr(command));
1331    }
1332    else if (command==iscp_.NETUSB_SONG_ELAPSEDTIME_QUERY)
1333    {
1334      System.out.println("Processing command="+iscp_.getCommandStr(command));
1335    }
1336    else if (command==iscp_.NETUSB_SONG_ARTIST_QUERY)
1337    {
1338      System.out.println("Processing command="+iscp_.getCommandStr(command));
1339    }
1340    else if (command==iscp_.NETUSB_SONG_ALBUM_QUERY)
1341    {
1342      System.out.println("Processing command="+iscp_.getCommandStr(command));
1343    }
1344    else if (command==iscp_.NETUSB_SONG_TITLE_QUERY)
1345    {
1346      System.out.println("Processing command="+iscp_.getCommandStr(command));
1347    }
1348    else if (command==iscp_.NETUSB_OP_DISPLAY)
1349    {
1350      System.out.println("Processing command="+iscp_.getCommandStr(command));
1351    }
1352
1353    retVal += "\n              \n       }";
1354    return retVal;
1355  }
1356
1357
1358  /**
1359   *  A method to simply abstract the Try/Catch required to put the current
1360   *  thread to sleep for the specified time in ms.
1361   *
1362   * @param  waitTime  the sleep time in milli seconds (ms).
1363   * @return           boolean value specifying if the sleep completed (true) or was interupted (false).
1364   */
1365  public boolean sleep(long waitTime)
1366  {
1367    boolean retVal = true;
1368    /*
1369     *  BLOCK for the spec'd time
1370     */
1371    try
1372    {
1373      Thread.sleep(waitTime);
1374    }
1375    catch (InterruptedException iex)
1376    {
1377      retVal = false;
1378    }
1379    return retVal;
1380  }
1381
1382
1383  /** gets the help as a String.
1384   * @return the helpMsg in String form
1385   **/
1386  private static String getHelpMsgStr() {return getHelpMsg().toString();}
1387
1388
1389  /** initializes and gets the helpMsg_
1390  class var.
1391   * @return the class var helpMsg_
1392   **/
1393  private static StringBuffer getHelpMsg()
1394  {
1395    helpMsg_ = new StringBuffer(SYSTEM_LINE_SEPERATOR);
1396    helpMsg_.append("---  WebARTS Eiscp Class  -----------------------------------------------------");
1397    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1398    helpMsg_.append("---  $Revision: 1308 $ $Date: 2020-02-02 10:41:39 -0800 (Sun, 02 Feb 2020) $ ---");
1399    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1400    helpMsg_.append("-------------------------------------------------------------------------------");
1401    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1402    helpMsg_.append("WebARTS Eiscp Class");
1403    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1404    helpMsg_.append("SYNTAX:");
1405    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1406    helpMsg_.append("   java ");
1407    helpMsg_.append(CLASSNAME);
1408    helpMsg_.append(" [hostIP] command [commandArgs]");
1409    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1410    helpMsg_.append("      - hostIP is optional and defaults to 10.0.0.203");
1411    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1412    helpMsg_.append("      - command is NOT optional");
1413    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1414    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1415    helpMsg_.append("Available Commands:");
1416    /* now add all the commands available */
1417    Iterator<String> it =iscp_.getIterator();
1418    while( it.hasNext())
1419    {
1420      helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1421      helpMsg_.append("-->   "+it.next());
1422    }
1423    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1424    helpMsg_.append("---------------------------------------------------------");
1425    helpMsg_.append("----------------------");
1426    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
1427
1428    return helpMsg_;
1429  }
1430
1431
1432  /** returns a text list of all the commands.
1433   * @return the list of commands as a string
1434   **/
1435  public static String getCommandList()
1436  {
1437    StringBuffer retVal = new StringBuffer("");
1438
1439    retVal.append("Available Commands:");
1440    /* now add all the commands available */
1441    Iterator<String> it =iscp_.getIterator();
1442    while( it.hasNext())
1443    {
1444      retVal.append(SYSTEM_LINE_SEPERATOR);
1445      retVal.append("-->   "+it.next());
1446    }
1447
1448    return retVal.toString();
1449  }
1450
1451
1452  /**
1453   * Class main commandLine entry method.
1454   **/
1455  public static void main(String [] args)
1456  {
1457    final String methodName = CLASSNAME + ": main()";
1458    Eiscp instance = new Eiscp(DEFAULT_EISCP_IP, DEFAULT_EISCP_PORT);
1459    int commandArg = 0;
1460
1461    /* Simple way af parsing the args */
1462    if (args ==null || args.length<1)
1463      System.out.println(getHelpMsgStr());
1464    else
1465    {
1466      if (args[0].equals("test"))
1467      {
1468        System.out.println("Testing Eiscp");
1469        String queryResponse =  instance.sendQueryCommand(iscp_.VOLUME_QUERY);
1470        String volumeResponse = instance.decipherVolumeResponse(queryResponse);
1471        System.out.println(volumeResponse);
1472        System.out.println();
1473
1474        instance.toggleMute();
1475        System.out.println();
1476        instance.sleep(750);
1477        instance.toggleMute();
1478      }
1479      else if (args[0].equals("dump"))
1480      {
1481        System.out.println("Dumping Eiscp Messages as binary values.");
1482        instance.dumpBinaryMessages();
1483      }
1484      else
1485      {
1486        /* Special case if Receiver IP was spec'd oncommandline  */
1487        if (args.length>1 && iscp_.getCommand(args[commandArg].toUpperCase())!=iscp_.VOLUME_SET)
1488          instance.setReceiverIP(args[commandArg++]);   // also bump the commandArg ref counter
1489
1490        // Parse the command
1491        int command = -1;
1492        String commandStr = "";
1493        // TODO: Set up a loop to handle multiple commands/args in one run with one socket connection
1494        command =iscp_.getCommand(args[commandArg].toUpperCase());  //returns -1 if not found
1495        commandStr=iscp_.getCommandStr(command);
1496        System.out.println("command: "+commandStr);
1497
1498        /* Special case VOLUME_SET command needs to parse a parameter. */
1499        if ( command == iscp_.VOLUME_SET ) instance.setVolume(Integer.parseInt(args[commandArg+1]));
1500
1501        String queryResponse = "";
1502        if (args[commandArg].toUpperCase().equals("DUMP"))
1503        {
1504          System.out.println("Dumping eIscp Messages as binary values.\n----------------------------------------------------");
1505          instance.dumpBinaryMessages();
1506        }
1507        else if (command!=-1)
1508        {
1509          /* It is a query command so send AND parse response */
1510          if(args[commandArg].toUpperCase().endsWith("QUERY")
1511             && !args[commandArg].toUpperCase().startsWith("NETUSB_"))
1512          {
1513            //send the command and get the response
1514            queryResponse = instance.sendQueryCommand(command, true, false);
1515            System.out.print("\nResponses: \n  " +queryResponse);
1516            if (queryResponse!=null && !queryResponse.equals(""))
1517            {
1518               if (command==iscp_.VOLUME_QUERY)
1519              {
1520                System.out.println(instance.decipherVolumeResponse(queryResponse));
1521              }
1522              else
1523                System.out.println("   ="+ iscp_.getCommandName(queryResponse.trim()));
1524            }
1525            else
1526              System.out.println("\nEMPTY");
1527          }
1528          else if( args[commandArg].toUpperCase().startsWith("NETUSB_"))
1529          {
1530            System.out.print("\nDEBUGGING: "+args[commandArg].toUpperCase());
1531            //send the command and get the response
1532            queryResponse = instance.sendQueryCommand(command, true, true);
1533            //System.out.print("\nResponses: \n  " +queryResponse);
1534            if (queryResponse!=null && !queryResponse.equals(""))
1535            {
1536              if (args[commandArg].toUpperCase().startsWith("NETUSB_"))
1537              {
1538                System.out.println(instance.decipherNetUsbResponse(command, queryResponse));
1539              }
1540            }
1541            else
1542              System.out.println("\nEMPTY");
1543          }
1544
1545          /* It is a basic change setting command (with no response) */
1546          else
1547          {
1548            instance.sendCommand(command); //send the command
1549          }
1550        }
1551        else
1552        {
1553          System.out.println(getHelpMsgStr()+"\n *!*!*!*! --> ");
1554          System.out.println(args[commandArg].toUpperCase() + " not found");
1555        }
1556      }
1557      instance.closeSocket();
1558    }
1559  } // main
1560
1561
1562  /**
1563   * get the class volume_.
1564   * @return the volume_
1565   **/
1566  public int getVolume()
1567  {
1568    return volume_;
1569  }
1570
1571
1572  /** sets the class volume_ AND sends the VOLUME_SET command to the Onkyo device.
1573   * @param volume the DECIMAL value to set the class volume_ and MUST be volume&lt;99 && volume&gt;-1</pre>
1574   **/
1575  public void setVolume(int volume)
1576  {
1577    if(volume<99 && volume>-1)
1578    {
1579      volume_ = volume;
1580      String vStr = ""+volume_;
1581      if(vStr.length()==1) vStr="0"+vStr;
1582      sendCommand(iscp_.VOLUME_SET, vStr);
1583    }
1584  }
1585
1586
1587  /** Toggles the MUTE setting..
1588   **/
1589  public void toggleMute()
1590  {
1591    String queryResponse =  sendQueryCommand(IscpCommands.MUTE_QUERY);
1592    if (true || debugging_) System.out.print("Responses: \n  " +queryResponse.trim() + "("+iscp_.getCommandName(queryResponse.trim())+")");
1593    if (  iscp_.getCommandName(queryResponse.trim()).equals("UNMUTE"))
1594    {
1595      if (true || debugging_) System.out.println("\nMuting .");
1596      sendCommand(IscpCommands.MUTE);
1597    }
1598    else
1599    {
1600      if (true || debugging_) System.out.println("\nUN-Muting .");
1601      sendCommand(IscpCommands.UNMUTE);
1602    }
1603  }
1604
1605} // class
1606
1607/*
1608Dumping eIscp Messages as binary values.
1609----------------------------------------------------
1610POWER_OFF: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;80;87;82;48;48;13
1611POWER_ON: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;80;87;82;48;49;13
1612POWER_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;80;87;82;81;83;84;78;13
1613MUTE: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;65;77;84;48;49;13
1614UNMUTE: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;65;77;84;48;48;13
1615MUTE_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;65;77;84;81;83;84;78;13
1616VOLUME_DOWN: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;77;86;76;68;79;87;78;13
1617VOLUME_DOWN1: 73;83;67;80;0;0;0;16;0;0;0;27;1;0;0;0;33;49;77;86;76;68;79;87;78;49;13
1618VOLUME_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;77;86;76;81;83;84;78;13
1619VOLUME_SET: 73;83;67;80;0;0;0;16;0;0;0;22;1;0;0;0;33;49;77;86;76;50;48;13
1620VOLUME_UP: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;77;86;76;85;80;13
1621VOLUME_UP1: 73;83;67;80;0;0;0;16;0;0;0;25;1;0;0;0;33;49;77;86;76;85;80;49;13
1622AUDIO_INFO_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;73;70;65;81;83;84;78;13
1623LISTEN_MODE_ALCHANSTEREO: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;48;67;13
1624LISTEN_MODE_AUDYSSEY_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;49;54;13
1625LISTEN_MODE_NEO_CINEMA_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;65;51;13
1626LISTEN_MODE_NEO_MUSIC_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;65;52;13
1627LISTEN_MODE_NEURAL_DIGITAL_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;65;54;13
1628LISTEN_MODE_NEURAL_SURROUND_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;65;53;13
1629LISTEN_MODE_PLII_GAME_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;65;50;13
1630LISTEN_MODE_PLII_MOVIE_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;65;48;13
1631LISTEN_MODE_PLII_MUSIC_DSX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;65;49;13
1632LISTEN_MODE_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;76;77;68;81;83;84;78;13
1633LISTEN_MODE_STEREO: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;48;48;13
1634LISTEN_MODE_THEATER_DIMENSIONAL: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;76;77;68;48;68;13
1635NETUSB_OP_0: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;48;13
1636NETUSB_OP_1: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;49;13
1637NETUSB_OP_2: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;50;13
1638NETUSB_OP_3: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;51;13
1639NETUSB_OP_4: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;52;13
1640NETUSB_OP_5: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;53;13
1641NETUSB_OP_6: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;54;13
1642NETUSB_OP_7: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;55;13
1643NETUSB_OP_8: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;56;13
1644NETUSB_OP_9: 73;83;67;80;0;0;0;16;0;0;0;23;1;0;0;0;33;49;78;84;67;57;13
1645NETUSB_OP_CAPS: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;67;65;80;83;13
1646NETUSB_OP_CHANDWN: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;67;72;68;78;13
1647NETUSB_OP_CHANUP: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;67;72;85;80;13
1648NETUSB_OP_DELETE: 73;83;67;80;0;0;0;16;0;0;0;28;1;0;0;0;33;49;78;84;67;68;69;76;69;84;69;13
1649NETUSB_OP_DISPLAY: 73;83;67;80;0;0;0;16;0;0;0;29;1;0;0;0;33;49;78;84;67;68;73;83;80;76;65;89;13
1650NETUSB_OP_DOWN: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;68;79;87;78;13
1651NETUSB_OP_FF: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;78;84;67;70;70;13
1652NETUSB_OP_LEFT: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;76;69;70;84;13
1653NETUSB_OP_MENU: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;77;69;78;85;13
1654NETUSB_OP_PAUSE: 73;83;67;80;0;0;0;16;0;0;0;27;1;0;0;0;33;49;78;84;67;80;65;85;83;69;13
1655NETUSB_OP_PLAY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;80;76;65;89;13
1656NETUSB_OP_RANDOM: 73;83;67;80;0;0;0;16;0;0;0;28;1;0;0;0;33;49;78;84;67;82;65;78;68;79;77;13
1657NETUSB_OP_REPEAT: 73;83;67;80;0;0;0;16;0;0;0;28;1;0;0;0;33;49;78;84;67;82;69;80;69;65;84;13
1658NETUSB_OP_RETURN: 73;83;67;80;0;0;0;16;0;0;0;28;1;0;0;0;33;49;78;84;67;82;69;84;85;82;78;13
1659NETUSB_OP_REW: 73;83;67;80;0;0;0;16;0;0;0;25;1;0;0;0;33;49;78;84;67;82;69;87;13
1660NETUSB_OP_RIGHT: 73;83;67;80;0;0;0;16;0;0;0;27;1;0;0;0;33;49;78;84;67;82;73;71;72;84;13
1661NETUSB_OP_SELECT: 73;83;67;80;0;0;0;16;0;0;0;28;1;0;0;0;33;49;78;84;67;83;69;76;69;67;84;13
1662NETUSB_OP_SETUP: 73;83;67;80;0;0;0;16;0;0;0;27;1;0;0;0;33;49;78;84;67;83;69;84;85;80;13
1663NETUSB_OP_STOP: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;83;84;79;80;13
1664NETUSB_OP_TOPMENU: 73;83;67;80;0;0;0;16;0;0;0;25;1;0;0;0;33;49;78;84;67;84;79;80;13
1665NETUSB_OP_TRACKDWN: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;84;82;68;78;13
1666NETUSB_OP_TRACKUP: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;67;84;82;85;80;13
1667NETUSB_OP_UP: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;78;84;67;85;80;13
1668NETUSB_PLAY_STATUS_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;83;84;81;83;84;78;13
1669NETUSB_SONG_ALBUM_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;65;76;81;83;84;78;13
1670NETUSB_SONG_ARTIST_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;65;84;81;83;84;78;13
1671NETUSB_SONG_ELAPSEDTIME_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;77;81;83;84;78;13
1672NETUSB_SONG_TITLE_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;73;81;83;84;78;13
1673NETUSB_SONG_TRACK_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;78;84;82;81;83;84;78;13
1674SOURCE_AM: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;53;13
1675SOURCE_AUX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;51;13
1676SOURCE_AUXILIARY: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;51;13
1677SOURCE_BLURAY: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;49;48;13
1678SOURCE_CD: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;51;13
1679SOURCE_COMPUTER: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;53;13
1680SOURCE_DOWN: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;83;76;73;68;79;87;78;13
1681SOURCE_DVR: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;48;13
1682SOURCE_FM: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;52;13
1683SOURCE_GAME: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;50;13
1684SOURCE_INTERETRADIO: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;56;13
1685SOURCE_MULTICH: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;51;48;13
1686SOURCE_MUSICSERVER: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;55;13
1687SOURCE_NETWORK: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;67;13
1688SOURCE_PC: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;53;13
1689SOURCE_PHONO: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;50;13
1690SOURCE_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;83;76;73;81;83;84;78;13
1691SOURCE_SATELLITE: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;49;13
1692SOURCE_SIRIUS: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;51;50;13
1693SOURCE_TAPE1: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;48;13
1694SOURCE_TAPE2: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;49;13
1695SOURCE_TUNER: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;54;13
1696SOURCE_UP: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;85;80;13
1697SOURCE_USB: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;57;13
1698SOURCE_USB_BACK: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;50;65;13
1699SOURCE_VIDEO5: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;73;48;52;13
1700VIDEO_INFO_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;73;70;86;81;83;84;78;13
1701VIDEO_WIDE_43: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;86;87;77;48;49;13
1702VIDEO_WIDE_AUTO: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;86;87;77;48;48;13
1703VIDEO_WIDE_FULL: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;86;87;77;48;50;13
1704VIDEO_WIDE_NEXT: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;86;87;77;85;80;13
1705VIDEO_WIDE_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;86;87;77;81;83;84;78;13
1706VIDEO_WIDE_SMARTZOOM: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;86;87;77;48;53;13
1707VIDEO_WIDE_WIDEZOOM: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;86;87;77;48;52;13
1708VIDEO_WIDE_ZOOM: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;86;87;77;48;51;13
1709ZONE2_POWER_ON: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;90;80;87;48;49;13
1710ZONE2_POWER_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;90;80;87;81;83;84;78;13
1711ZONE2_POWER_SBY: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;90;80;87;48;48;13
1712ZONE2_SOURCE_AUX: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;90;48;51;13
1713ZONE2_SOURCE_BLURAY: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;90;49;48;13
1714ZONE2_SOURCE_COMPUTER: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;90;48;53;13
1715ZONE2_SOURCE_DVR: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;90;48;48;13
1716ZONE2_SOURCE_GAME: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;90;48;50;13
1717ZONE2_SOURCE_QUERY: 73;83;67;80;0;0;0;16;0;0;0;26;1;0;0;0;33;49;83;76;90;81;83;84;78;13
1718ZONE2_SOURCE_SATELLITE: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;90;48;49;13
1719ZONE2_SOURCE_VIDEO5: 73;83;67;80;0;0;0;16;0;0;0;24;1;0;0;0;33;49;83;76;90;48;52;13
1720*/