001/* 002 * $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/isy/LastFMRestRequester.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; 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; 038 039import ca.bc.webarts.tools.RestRequester; 040import ca.bc.webarts.widgets.Quick; 041 042import org.apache.commons.codec.binary.Base64; 043 044import nu.xom.Attribute; 045import nu.xom.Builder; 046import nu.xom.Document; 047import nu.xom.Element; 048import nu.xom.Elements; 049import nu.xom.Node; 050import nu.xom.ParsingException; 051import nu.xom.ValidityException; 052import nu.xom.Serializer; 053 054 055/** 056 * This class wraps the communication to the REST interface of a 057 * <a href="http://www.last.fm/api/rest">Last.fm</a>. 058 * It provides many prebuilt java methods that wrap a specific REST call or you can request any one that 059 * is specified in the REST API - 060 * <a href="http://www.last.fm/api"> 061 * http://www.last.fm/api</a>.<br /> 062 * Written by Tom Gutwin - WebARTS Design.<br /> 063 * Copyright © 2016 WebARTS Design, North Vancouver Canada<br /> 064 * <a href="http://www.webarts.ca">http://www.webarts.ca</a> 065 * 066 * @author Tom B. Gutwin 067 **/ 068public class LastFMRestRequester extends RestRequester 069{ 070 protected static final String CLASSNAME = "ca.bc.webarts.tools.LastFMRestRequester"; //ca.bc.webarts.widgets.Util.getCurrentClassName(); 071 public static final String LOG_TAG = "\n"+CLASSNAME; //+"."+ca.bc.webarts.android.Util.getCurrentClassName(); 072 073 /** DEFAULT ISY994 IP address to use: 10.0.0.207 .**/ 074 protected static final String DEFAULT_IP = "ws.audioscrobbler.com"; 075 /** DEFAULT Last.FM username to use: admin .**/ 076 protected static final String DEFAULT_USERNAME = "admin"; 077 /** DEFAULT Last.FM password to use: admin .**/ 078 protected static final String DEFAULT_PASSWORD = "admin"; 079 /** DEFAULT ISY994 rest URL to start the URL path: /rest .**/ 080 protected static final String DEFAULT_REST_URL_PATHSTR = "/2.0"; 081 protected static final String DEFAULT_API_KEY="93df6c849563eef04d7d48db5950d0c7"; // jOggPlayer Kkey 082 protected static final String DEFAULT_REST_RESULTS_FORMAT="json"; 083 084 protected static final String TOMS_IP = DEFAULT_IP; 085 protected static final String TOMS_USERNAME = "tgutwin"; 086 protected static final String TOMS_PASSWORD = "AcrobatiC8"; 087 088 protected static StringBuilder helpMsg_ = new StringBuilder(SYSTEM_LINE_SEPERATOR); 089 protected static boolean debugOut_ = false; 090 /** flag to indicate the use of TOMS_isy IP, userID, and password. **/ 091 protected static boolean tomsID_ = true; 092 093 094 protected String apiKey_ = DEFAULT_API_KEY; 095 protected String userID_ = TOMS_USERNAME; 096 protected String restFormat_ = DEFAULT_REST_RESULTS_FORMAT; 097 098 /** The start path to use in therest URL. Over-ride this if you extend this class. **/ 099 protected String restUrlPath_ = DEFAULT_REST_URL_PATHSTR+ 100 "/?api_key="+apiKey_+ 101 "&user="+userID_+ 102 "&format="+restFormat_+ 103 "&method="+user.getrecenttracks; 104 105 /** 106 * Default constructor that authenticates the default ISY with the default user password (using the class vars) 107 * UNLESS the tomsID_ class var is true to over-ride with TOMS _isy IP, userID, and password. 108 * TOMS settings get 1st priority, and DEFAULTS if {@link #tomsID_ tomsID_} class var is false. 109 * 110 * @see #DEFAULT_IP 111 * @see #DEFAULT_USERNAME 112 * @see #DEFAULT_PASSWORD 113 **/ 114 public LastFMRestRequester() 115 { 116 authenticating_=true; 117 buildRestUrl(); 118 //setBaseUrl( "http://"+(tomsID_?TOMS_IP:DEFAULT_IP)+restUrlPath_); 119 setUsername( (tomsID_?TOMS_USERNAME:DEFAULT_USERNAME)); 120 setPassword( (tomsID_?TOMS_PASSWORD:DEFAULT_PASSWORD)); 121 } 122 123 124 /** 125 * Default constructor that authenticates and connects the ISY with a choice of either the default user password 126 * (using the class vars) or with TOMS _isy IP, userID, and password.. 127 * 128 * @see #DEFAULT_IP 129 * @see #DEFAULT_USERNAME 130 * @see #DEFAULT_PASSWORD 131 **/ 132 public LastFMRestRequester(boolean useDefault) 133 { 134 tomsID_=!useDefault; 135 authenticating_=true; 136 buildRestUrl(); 137 //setBaseUrl( "http://"+(tomsID_?TOMS_IP:DEFAULT_IP)+restUrlPath_); 138 setUsername( (tomsID_?TOMS_USERNAME:DEFAULT_USERNAME)); 139 setPassword( (tomsID_?TOMS_PASSWORD:DEFAULT_PASSWORD)); 140 } 141 142 143 /** 144 * Constructor to customize all connection settings. 145 * 146 **/ 147 public LastFMRestRequester(String server, String user, String pass) 148 { 149 setBaseUrl( "http://"+server+restUrlPath_); 150 authenticating_=true; 151 setUsername(user); 152 setPassword( pass); 153 } 154 155 156 /** 157 * Set Method for class field {@link #tomsID_ tomsID_}. 158 * 159 * @param tomsId is the value to set this class field to. 160 * 161 **/ 162 public static void setTomsID(boolean tomsId) 163 { 164 tomsID_ = tomsId; 165 } // setTomsIsy Method 166 167 168 /** 169 * Get Method for class field 'tomsID_'. 170 * 171 * @return boolean - The value the class field {@link #tomsID_ tomsID_}. 172 * 173 **/ 174 public static boolean getTomsID() 175 { 176 return tomsID_; 177 } // getTomsIsy Method 178 179 180 /** 181 * Set Method for class field 'restUrlPath_'. 182 * 183 * @param restUrlPath_ is the value to set this class field to. 184 * 185 **/ 186 public void setRestUrlPath(String restUrlPath) 187 { 188 restUrlPath_ = restUrlPath; 189 } // setRestUrlPath Method 190 191 192 /** 193 * Get Method for class field 'restUrlPath_'. 194 * 195 * @return String - The value the class field 'restUrlPath_'. 196 * 197 **/ 198 public String getRestUrlPath() 199 { 200 return restUrlPath_; 201 } // getRestUrlPath Method 202 203 204 /** 205 * 206 **/ 207 private void buildRestUrl() 208 { 209 restUrlPath_ = DEFAULT_REST_URL_PATHSTR+ 210 "/?api_key="+apiKey_+ 211 "&user="+userID_+ 212 "&format="+restFormat_+ 213 "&method="+user.getrecenttracks; 214 setBaseUrl( "http://"+(tomsID_?TOMS_IP:DEFAULT_IP)+restUrlPath_); 215 } 216 217 218 /** 219 * 220 **/ 221 public StringBuilder sendApiCommand(String apiCommand) 222 { 223 buildRestUrl(); 224 return serviceGet("&method="+apiCommand); 225 } 226 227 228 /** 229 * 230 **/ 231 public String sendAuthGetToken() 232 { 233 String tokenStr = ""; 234 StringBuilder retVal = sendApiCommand("auth.getToken"); 235 System.out.println("Response for 'auth.getToken'"); 236 System.out.println(retVal.toString()); 237 238 return tokenStr; 239 } 240 241 242 /** Sends a REST call to get a request token to later be authorized by the user. 243 * This will return a token. To see the response format check the method documentation page. 244 * The token is not authorized by the user at this stage. 245 * Authentication Tokens are API account specific. They are valid for 60 minutes from the moment they are granted. 246 * @return a token to be used in the user AUTH url. 247 **/ 248 public StringBuilder requestToken() 249 { 250 return serviceGet("/nodes"); 251 } 252 253 254 255 256 /** Check connectivity to the ISY specified by the class parms. 257 * @return true or false 258 **/ 259 public boolean canConnect() 260 { 261 if(debugOut_) System.out.println(LOG_TAG+".canConnect("+getBaseUrl()+", "+getUsername()+", "+getPassword()+")"); 262 263 boolean retVal = false; 264 if(isInit()) 265 { 266 try 267 { 268 if(debugOut_) System.out.println(LOG_TAG+".init = true"); 269 String usrlStr = (baseUrl_+"/sys").replace(" " ,"%20"); 270 URL url = new URL(usrlStr); 271 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 272 conn.setRequestMethod("GET"); 273 if(acceptJSON_) 274 conn.setRequestProperty("Accept", "application/json"); 275 else 276 conn.setRequestProperty("Accept", "application/xml"); 277 278 if (authenticating_) 279 { 280 //BASE64Encoder enc = new sun.misc.BASE64Encoder(); 281 String userpassword = username_ + ":" + password_; 282 //String encodedAuthorization = android.util.Base64.encodeToString( userpassword.getBytes(), android.util.Base64.DEFAULT ); 283 String encodedAuthorization = new String(Base64.encodeBase64( (userpassword.getBytes()) )); 284 conn.setRequestProperty("Authorization", "Basic "+ encodedAuthorization); 285 } 286 287 if (conn.getResponseCode() == 200) 288 { 289 retVal=true; 290 } // valid http response code 291 conn.disconnect(); 292 } 293 catch (MalformedURLException e) 294 { 295 e.printStackTrace(); 296 } 297 catch (IOException e) 298 { 299 e.printStackTrace(); 300 } 301 } 302 return retVal; 303 } 304 305 306 /** returns the on/off/% fora specified node address. 307 * @return on=100, off=0, or any number in between 0-100 308 **/ 309 public int deviceAddressStatus(String addr) 310 { 311 parseAllNodes(); // fast return if already parsed 312 int retVal = -1; 313 StringBuilder resp = serviceGet("/status/"+addr); 314 /* The response holds and attribute called formatted that can be one of Off, On , % */ 315 /* <property id="ST" value="114" formatted="45" uom="%/on/off"/> */ 316 if (resp!=null) 317 { 318 String respStr = resp.toString(); 319 int fSpot = respStr.indexOf("formatted"); 320 String fVal = respStr.substring(fSpot+11,respStr.indexOf("\"", fSpot+12)); 321 if(debugOut_) System.out.println("STATUS: "+ addr+": "+fVal+"\n"+resp.toString()); 322 if(fVal.equalsIgnoreCase("Off") ) retVal = 0; 323 else if (fVal.equalsIgnoreCase("On") ) retVal = 100; 324 else if (fVal.trim().equals("") ) retVal = -1; 325 else if (fVal.trim().indexOf(".")!=-1 ) 326 try{retVal = Integer.parseInt(fVal.substring(0,fVal.indexOf(".")));} 327 catch(Exception ex){System.out.println("ERROR pulling the status value from "+fVal+" "+fVal.substring(0,fVal.indexOf(".")));} 328 else 329 try{retVal = Integer.parseInt(fVal);} 330 catch(Exception ex){System.out.println("ERROR pulling the int status value from "+fVal);} 331 } 332 333 return retVal; 334 } 335 336 337 /** 338 * Class main commandLine entry method that has a test command and some convienience commands, as well as a pure rest command. 339 **/ 340 public static void main(String [] args) 341 { 342 final String methodName = CLASSNAME + ": main()"; 343 LastFMRestRequester instance = new LastFMRestRequester(); 344 345 /* Simple way af parsing the args */ 346 if (args ==null || args.length<1) 347 System.out.println(getHelpMsgStr()); 348 /* *************************************** */ 349 else 350 { 351 if (args[0].equalsIgnoreCase("test")) 352 { 353 instance.testCMD(args); 354 } 355 /* *************************************** */ 356 else if (args[0].equalsIgnoreCase("listNodes")) 357 { 358 instance.listNodesCMD(args); 359 } 360 /* *************************************** */ 361 else if (args[0].equalsIgnoreCase("getProperty")) 362 { 363 instance.getPropertyCMD(args); 364 } 365 /* *************************************** */ 366 else if (args[0].equalsIgnoreCase("toggle")) 367 { 368 instance.toggleCMD(args); 369 } 370 /* *************************************** */ 371 else if (args[0].equalsIgnoreCase("status")) 372 { 373 instance.statusCMD(args); 374 } 375 /* *************************************** */ 376 else 377 { 378 instance.restCMD(args); 379 } 380 } 381 } // main 382 383 384 /** 385 * commandLine command executor method for the test Command. 386 * @param args the array of commandLine args that got passed in 387 **/ 388 protected void testCMD(String [] args) 389 { 390 final String methodName = CLASSNAME + ": testCMD(String [])"; 391 parseAllNodes(); // fast return if already parsed 392 393 394 System.out.println("Testing Rest Service: "+ "/sys"); 395 StringBuilder resp = serviceGet("/sys"); 396 System.out.println(resp.toString()); System.out.println(); 397 398 } 399 400 401 /** 402 * commandLine command executor method for the default rest Command. 403 * It treats each arg as a part of a single rest command and passes it along to the ISY. 404 * @param args the array of commandLine args that got passed in 405 **/ 406 protected void restCMD(String [] args) 407 { 408 final String methodName = CLASSNAME + ": restCMD(String [])"; 409 // Parse the command 410 String allcommands = args[0]; 411 for (int i=1;i< args.length;i++) allcommands+=" "+args[i]; 412 System.out.print("Sending Rest Service: "+allcommands); 413 String passedCommand = (allcommands.startsWith(restUrlPath_+"/")?allcommands.substring(restUrlPath_.length()):allcommands); 414 System.out.println(" ("+passedCommand+")"); 415 passedCommand = (passedCommand.startsWith("/")?passedCommand:"/"+passedCommand); 416 StringBuilder resp = serviceGet(passedCommand); 417 if (resp!=null) 418 { 419 System.out.println(responseIndenter(resp).toString()); 420 System.out.println(); 421 } 422 else 423 { 424 System.out.println("Response Error"); 425 System.out.println(); 426 } 427 428 } 429 430 431 /** 432 * commandLine command executor method for the getProperty Command. 433 * @param args the array of commandLine args that got passed in 434 **/ 435 protected void getPropertyCMD(String [] args) 436 { 437 final String methodName = CLASSNAME + ": getPropertyCMD(String [])"; 438 439 440 System.out.println("ISY Rest Services: "+ "node property"); 441 if (args.length>2 && args[0].equalsIgnoreCase("getProperty")) parseAllNodes(); 442 if (isyNodes_!=null && args.length>2) 443 { 444 String nName = args[1]; 445 for (int i=2;i< args.length-1;i++) nName+=" "+args[i]; 446 String pName = args[args.length-1]; 447 448 IsyDeviceProperty isyProp = deviceProperty(nName, pName); 449 String propElementStr = isyProp.getElementStr(); 450 System.out.println(propElementStr); 451 System.out.println(" Value="+isyProp.getValue()); 452 System.out.println(" Formatted Value="+isyProp.getFormatted()+" "+isyProp.getUom()); 453 System.out.println(" Integer Value="+isyProp.getIntValue()); 454 } 455 else 456 System.out.println("ERROR on comnmandLine: getProperty command requires a nodeName and a propertyName"); 457 } 458 459 460 /** 461 * Template method for future commandLine command executor methods. 462 * @param args the array of commandLine args that got passed in 463 **/ 464 protected void templateCMD(String [] args) 465 { 466 final String methodName = CLASSNAME + ": testCMD(String [])"; 467 468 } 469 470 471 /** gets the help as a String. 472 * @return the helpMsg in String form 473 **/ 474 protected static String getHelpMsgStr() {return getHelpMsg().toString();} 475 476 477 /** initializes and gets the helpMsg_ 478 class var. 479 * @return the class var helpMsg_ 480 **/ 481 protected static StringBuilder getHelpMsg() 482 { 483 helpMsg_ = new StringBuilder(SYSTEM_LINE_SEPERATOR); 484 helpMsg_.append("--- WebARTS "+CLASSNAME+" Class -----------------------------------------------------"); 485 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 486 helpMsg_.append("--- + $Revision: 1091 $ $Date: 2016-05-29 16:26:54 -0700 (Sun, 29 May 2016) $ ---"); 487 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 488 helpMsg_.append("-------------------------------------------------------------------------------"); 489 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 490 helpMsg_.append("WebARTS ca.bc.webarts.tools.LastFMRestRequester Class"); 491 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 492 helpMsg_.append("SYNTAX:"); 493 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 494 helpMsg_.append(" java "); 495 helpMsg_.append(CLASSNAME); 496 helpMsg_.append(" command or {restCommand}"); 497 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 498 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 499 helpMsg_.append("Available commands:"); 500 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 501 helpMsg_.append(" test"); 502 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 503 helpMsg_.append(" listNodes "); 504 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 505 helpMsg_.append(" status noneName"); 506 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 507 helpMsg_.append(" getProperty nodeName propertyName"); 508 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 509 helpMsg_.append(" toggle nodeName"); 510 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 511 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 512 helpMsg_.append("Available restCommands:"); 513 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 514 helpMsg_.append(" see: http://www.last.fm/api"); 515 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 516 helpMsg_.append(" Example: java ca.bc.webarts.android.LastFMRestRequester /sys "); 517 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 518 helpMsg_.append("---------------------------------------------------------"); 519 helpMsg_.append("----------------------"); 520 helpMsg_.append(SYSTEM_LINE_SEPERATOR); 521 522 return helpMsg_; 523 } 524 525}