Writing custom client apps...

TripleT

Active member
Is there an API for writing custom clients? For example if I want to write my own program to run on Windows Phone and talk to the Apex?
 
I think most of us have just reverse engineered the XML & HTML/Form/POST based on the Apex web control pages.
 
edit:

No connection whatsoever. You were in violation of the [ua]
 
Last edited by a moderator:
Code to pull current XML status:
Code:
	public static void fetchXMLStatus(Context activityContext, Uri controllerUri)
	throws ApexCommException {

		ContentResolver resolver = activityContext.getContentResolver();
		Cursor cursor = null;

		String username = null;
		String password = null;
		String apexBaseURL = null;
		String controllerType = null;

		// Poll the database for facts about this controller
		try {
			cursor = resolver.query(controllerUri, PROJECTION_CONTROLLERS, null, null, null);
			if (cursor != null && cursor.moveToFirst()) {
				username = cursor.getString(COL_CNT_USERNAME);
				password = cursor.getString(COL_CNT_PASSWORD);
				apexBaseURL = cursor.getString(COL_CNT_URL);
				controllerType = cursor.getString(COL_CNT_DEVTYPE);
			}
		} catch (SQLException e) {
			Log.e(LOG_TAG, "refreshProbeOutletXML: getting controller facts.", e);	
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}

		InputStream is = null;

		// First lets pull the XML status page from the Apex controller
		if(!((username == null) || (password == null) || (apexBaseURL == null))) {
			try {
				// for this function we need to append to the URL.  I should really
				// check if the "/" was put on the end by the user here to avoid 
				// possible errors.
				if(!apexBaseURL.endsWith("/")) {
					String tmp = apexBaseURL + "/";
					apexBaseURL = tmp;
				}

				// oh, we should also check if it starts with an "http://"
				if(!apexBaseURL.startsWith("http://")) {
					String tmp = "http://" + apexBaseURL;
					apexBaseURL = tmp;
				}

				String apexURL = apexBaseURL + "cgi-bin/status.xml";

				//Create credentials for basic auth
				UsernamePasswordCredentials c = new UsernamePasswordCredentials(username,password);

				//create a basic credentials provider and pass the credentials
				BasicCredentialsProvider cP = new BasicCredentialsProvider();
				cP.setCredentials(AuthScope.ANY, c );

				//Set credentials provider for our default http client so it will use those credentials
				// Create defaulthttpclient for communications with the Apex
				DefaultHttpClient http = new DefaultHttpClient();
				http.setCredentialsProvider(cP);

				//get what we want from the target site
				HttpResponse httpRes = http.execute(new HttpGet(apexURL));

				// Check if server response is valid
				StatusLine status = httpRes.getStatusLine();
				if (status.getStatusCode() != HTTP_STATUS_OK) {
					throw new ApexCommException("Invalid response from server: " + status.toString());
				}
				is =httpRes.getEntity().getContent();
			} catch (Exception e) {
				Log.e("Apex", "HTTP failed", e);
				throw new ApexCommException("HTTP connection failed.");
			}

			// Then we'll parse the XML for the probes which we'll then display on the widget
			// Maybe we should parse more data too such as the time-stamp.  That might be good
			// to add next to the "Apex" on the widget
			// The parsing helper class will actually write the data to the database.
			try {
				/* Get a SAXParser from the SAXPArserFactory. */
				SAXParserFactory spf = SAXParserFactory.newInstance();
				SAXParser sp = spf.newSAXParser();

				/* Get the XMLReader of the SAXParser we created. */
				XMLReader xr = sp.getXMLReader();

				/* Create a new ContentHandler and apply it to the XML-Reader*/
				// Now the content handler has the probe list, need to move this up
				// to this Apex class!!!
				ApexXMLStateHandler myProbeOutletXMLHandler = new ApexXMLStateHandler(activityContext, controllerUri);

				xr.setContentHandler(myProbeOutletXMLHandler);

				/* Parse the xml-data from our URL. */
				xr.parse(new InputSource(is));
				/* Parsing has finished. */

				// After parsing, the parse-handler should have populated the database.

			} catch (Exception e) {
				Log.e("ApexWidget2x1", "XML Parser failed", e);
				throw new ApexCommException("XML Parser failed.");
			}
		}
		else {
			//return "User/Password and/or Apex URL not set in preferences.";
			throw new ApexCommException("User/Password and/or Apex URL not set in preferences.");
		}
	}

Which uses the Java XML helper class: exit - sorry, the XML tags are getting interpreted by the browser so the comments are not showing correctly...
Code:
// This is a class that will parse the Apex XML response.
// More info on Android XML parsers is available on the net.
//
// The format of the Apex response looks like this:
//
//	<status software="4.02_2H10" hardware="1.0">
//		<hostname>Apex</hostname>
//		<serial>AC4:01405</serial>
//		<date>04/13/2010 09:43:44</date>
//
//		<power>
//		<failed>03/13/2010 09:25:29</failed>
//		<restored>03/14/2010 01:48:26</restored>
//		</power>
//
//		<probes>
//
//			<probe>
//		  		<name>Temp</name>
//				<value>78.1</value>
//			</probe>
//
//			<probe>
//				<name>pH</name>
//				<value>8.02</value>
//			</probe>
//
//			<probe>
//				<name>Amp_3</name>
//				<value>01.0 </value>
//			</probe>
//		</probes>
//
//		<outlets>
//
//			<outlet>
//				<name>WhiteLEDs</name>
//				<state>MaxWhite</state>
//			</outlet>
//
//			<outlet>
//				<name>BlueLEDs</name>
//				<state>MaxBlue</state>
//			</outlet>
//
//			<outlet>
//				<name>VarSpd3_I3</name>
//				<state>PF3</state>
//			</outlet>
//
//			<outlet>
//				<name>VarSpd4_I4</name>
//				<state>PF4</state>
//			</outlet>
//
//			<outlet>
//				<name>SndAlm_I6</name>
//				<state>AOF</state>
//			</outlet>
//
//			<outlet>
//				<name>SndWrn_I7</name>
//				<state>AOF</state>
//			</outlet>
//
//			<outlet>
//				<name>EmailAlm_I5</name>
//				<state>AOF</state>
//			</outlet>
//
//			<outlet>
//				<name>Actinics</name>
//				<state>AON</state>
//			</outlet>
//
//			<outlet>
//				<name>MH</name>
//				<state>AOF</state>
//			</outlet>
//
//			<outlet>
//				<name>T5s</name>
//				<state>AON</state>
//			</outlet>
//
//			<outlet>
//				<name>TunzeLeft</name>
//				<state>AON</state>
//			</outlet>
//
//			<outlet>
//				<name>Heater</name>
//				<state>AOF</state>
//			</outlet>
//
//			<outlet>
//				<name>FugeLights</name>
//				<state>AOF</state>
//			</outlet>
//
//			<outlet>
//				<name>ClosedLoop</name>
//				<state>AON</state>
//			</outlet>
//
//			<outlet>
//				<name>TunzeRight</name>
//				<state>AON</state>
//			</outlet>
//		</outlets>
//	</status>  

public class ApexXMLStateHandler extends DefaultHandler{

	// ===========================================================
	// Fields
	// ===========================================================
	private static final boolean LOGD = true;
	private static final String LOG_TAG = "ApexXMLStateHandler";

	private boolean in_date_tag = false;
	private boolean in_serial_tag = false;
	private boolean in_probes_tag = false;
	private boolean in_outlets_tag = false;
	private boolean in_probe_tag = false;
	private boolean in_outlet_tag = false;
	private boolean in_name_tag = false;
	private boolean in_value_tag = false;
	private boolean in_state_tag = false;
	
	private StringBuilder builder;
	
	// Possible fields for a class that might contain all the Apex stats...
	public Date timeStamp;
	public String deviceType;
	
	private static final String[] PROJECTION_PROBES = new String[] {
		ProbesColumns.PROBE_ID,
		ProbesColumns.CONTROLLER_ID,
		ProbesColumns.PROBENAME,
		ProbesColumns.UNITS,
	};
	private static final int COL_PRB_PID = 0;
	private static final int COL_PRB_CID = 1;
	private static final int COL_PRB_NAME = 2;
	private static final int COL_PRB_UNITS = 3;

	
	// This is used to hold the current probe or outlet name so that when
	// we get to the value/state we can add the appropriate object to the 
	// database.
	private String currentName = null;

	private Context context;
	Uri controllerUri;

	public ApexXMLStateHandler(Context ctxt, Uri ctlr) {
		context = ctxt;
		controllerUri = ctlr;
	}
	
	// ===========================================================
	// Methods
	// ===========================================================
	@Override
	public void startDocument() throws SAXException {
		super.startDocument();
		builder = new StringBuilder();
	}

	@Override
	public void endDocument() throws SAXException {
		// Nothing to do
	}

	/** Gets be called on opening tags like:
	 * <tag>
	 * Can provide attribute(s), when xml is of the form:
	 *     <tag attribute="attributeValue">*/
	
	// For us, we'll just note where we are in the tree.
	// This knowledge will help us when we close tags later.
	@Override
	public void startElement(String namespaceURI, String localName,
			String qName, Attributes atts) throws SAXException {
		super.startElement(namespaceURI, localName, qName, atts);
		if (localName.equalsIgnoreCase("date")) {
			this.in_date_tag = true;
		} else if (localName.equalsIgnoreCase("serial")) {
			this.in_serial_tag = true;
		} else if (localName.equalsIgnoreCase("probes")) {
			this.in_probes_tag = true;
		}else if (localName.equalsIgnoreCase("outlets")) {
			this.in_outlets_tag = true;
		}else if (localName.equalsIgnoreCase("probe")) {
			this.in_probe_tag = true;
		}else if (localName.equalsIgnoreCase("outlet")) {
			this.in_outlet_tag = true;
		}else if (localName.equalsIgnoreCase("name")) {
			this.in_name_tag = true;
		}else if (localName.equalsIgnoreCase("value")) {
			this.in_value_tag = true;
		}else if (localName.equalsIgnoreCase("state")) {
			this.in_state_tag = true;
		}
	}

	/** Gets be called on closing tags like:
	 * </tag> */
	@Override
	public void endElement(String namespaceURI, String localName, String qName)
	throws SAXException {

		super.endElement(namespaceURI, localName, qName);

		// For each end-tag, we'll check to make sure we are where we think we should
		// be by checking the state variables.  If in the correct state, we'll do the 
		// appropriate action for that end-tag.  We'll also need to update the state.
		if (localName.equalsIgnoreCase("value")) {
			if(this.in_probes_tag & this.in_probe_tag & (currentName!=null)) {
				/////////////////////////////////
				// We've got a value which we assume came after a name, so process the name/value pair
				/////////////////////////////////
				// Step 1 is to get the probe record number so we can link to the proper probe
				ContentResolver resolver = context.getContentResolver();
				Cursor cursor = null;
				Integer parentRecord = 0;
				String currentProbeValueString = builder.toString();
				//float currentProbeValue = Float.parseFloat(currentProbeValueString);
				Uri activeProbesUri = Uri.withAppendedPath(controllerUri, Probes.TWIG_PROBE_NAME);
				Uri probeByNameUri = Uri.withAppendedPath(activeProbesUri,currentName);
				int controllerId = Integer.parseInt(controllerUri.getPathSegments().get(1));

				try {
					cursor = resolver.query(probeByNameUri, PROJECTION_PROBES, null, null, null);
					if (cursor == null || !cursor.moveToFirst()) {
						Log.d(LOG_TAG, "Probe query did not return an existing probe.");	
						Log.d(LOG_TAG, "Inserting a new one.");	
						// if query fails then what?  We'll need to insert a probe record prior
						// to inserting this data record
						ContentValues values = new ContentValues();
						//values.put(BaseColumns._ID, xxx);  auto-generate key
						values.put(ProbesColumns.CONTROLLER_ID, controllerId);
						values.put(ProbesColumns.PROBENAME, currentName);
						values.put(ProbesColumns.UNITS, "N/A");
						values.put(ProbesColumns.TYPE, 1); // 1=probe, 0=outlet
						Uri insertProbeUri = Uri.withAppendedPath(controllerUri, Probes.TWIG_PROBES);
						resolver.insert(insertProbeUri, values);
						
						// ugh, now I have to get the record again??
						if (cursor != null) {
							cursor.close();
						}
						cursor = resolver.query(probeByNameUri, PROJECTION_PROBES, null, null, null);
						if (cursor == null || !cursor.moveToFirst()) {
								// This is a serious error, we just put it in, it must be there.
						}
					} else {
						Log.d(LOG_TAG, "Probe query returned an existing probe.");	
					}
					// grab the probe record #
					parentRecord = cursor.getInt(COL_PRB_PID);
				} catch (SQLException e) {
					Log.e("Database error", "Couldn't parse the returned date field", e);	
				}
				finally {
					if (cursor != null) {
						cursor.close();
					}
				} // end of database query for the probe by name

				// Step #2 will be to insert the new records
				ContentValues values2 = new ContentValues();
				//values.put(BaseColumns._ID, xxx);  auto-generate key
				values2.put(ProbeDataColumns.PROBE_ID, parentRecord);
				values2.put(ProbeDataColumns.CONTROLLER_ID, controllerId); // redundant but easier without join capability
				values2.put(ProbeDataColumns.PROBENAME, currentName); // redundant but easier without join capability
				values2.put(ProbeDataColumns.VALUE, currentProbeValueString);
				values2.put(ProbeDataColumns.TIMESTAMP, timeStamp.getTime());
				values2.put(ProbeDataColumns.DATUM_TYPE, 1); // 1=probe, 0=outlet
				Uri probeUri = Uri.withAppendedPath(controllerUri, Probes.TWIG_PROBES);
				Uri probeXUri = Uri.withAppendedPath(probeUri, parentRecord.toString());
				Uri insertProbeDataUri = Uri.withAppendedPath(probeXUri, ProbeData.TWIG_PROBE_DATA);
				resolver.insert(insertProbeDataUri, values2);

				/////////////////////////////////
				currentName="";
			}
			this.in_value_tag = false;
		}else if (localName.equalsIgnoreCase("state")) {
			if(this.in_outlets_tag & this.in_outlet_tag & (currentName!=null)) {
				/////////////////////////////////
				// We've got a value which we assume came after a name, so process the name/value pair
				/////////////////////////////////
				// Step 1 is to get the probe record number so we can link to the proper probe
				ContentResolver resolver = context.getContentResolver();
				Cursor cursor = null;
				Integer parentRecord = 0;
				String currentProbeValueString = builder.toString();
				//float currentProbeValue = Float.parseFloat(currentProbeValueString);
				Uri activeProbesUri = Uri.withAppendedPath(controllerUri, Probes.TWIG_PROBE_NAME);
				Uri probeByNameUri = Uri.withAppendedPath(activeProbesUri,currentName);
				int controllerId = Integer.parseInt(controllerUri.getPathSegments().get(1));

				try {
					cursor = resolver.query(probeByNameUri, PROJECTION_PROBES, null, null, null);
					if (cursor == null || !cursor.moveToFirst()) {
						Log.d(LOG_TAG, "Probe query did not return an existing probe.");	
						Log.d(LOG_TAG, "Inserting a new one.");	
						// if query fails then what?  We'll need to insert a probe record prior
						// to inserting this data record
						ContentValues values = new ContentValues();
						//values.put(BaseColumns._ID, xxx);  auto-generate key
						values.put(ProbesColumns.CONTROLLER_ID, controllerId);
						values.put(ProbesColumns.PROBENAME, currentName);
						values.put(ProbesColumns.TYPE, 0); // 1=probe, 0=outlet
						values.put(ProbesColumns.UNITS, "N/A");
						Uri insertProbeUri = Uri.withAppendedPath(controllerUri, Probes.TWIG_PROBES);
						resolver.insert(insertProbeUri, values);
						
						// ugh, now I have to get the record again??
						if (cursor != null) {
							cursor.close();
						}
						cursor = resolver.query(probeByNameUri, PROJECTION_PROBES, null, null, null);
						if (cursor == null || !cursor.moveToFirst()) {
								// This is a serious error, we just put it in, it must be there.
						}
					} else {
						Log.d(LOG_TAG, "Probe query returned an existing probe.");	
					}
					// grab the probe record #
					parentRecord = cursor.getInt(COL_PRB_PID);
				} catch (SQLException e) {
					Log.e("Database error", "Couldn't parse the returned date field", e);	
				}
				finally {
					if (cursor != null) {
						cursor.close();
					}
				} // end of database query for the probe by name

				// Step #2 will be to insert the new records
				ContentValues values2 = new ContentValues();
				//values.put(BaseColumns._ID, xxx);  auto-generate key
				values2.put(ProbeDataColumns.PROBE_ID, parentRecord);
				values2.put(ProbeDataColumns.CONTROLLER_ID, controllerId); // redundant but easier without join capability
				values2.put(ProbeDataColumns.PROBENAME, currentName); // redundant but easier without join capability
				values2.put(ProbeDataColumns.VALUE, currentProbeValueString.trim());
				values2.put(ProbeDataColumns.VALUE, currentProbeValueString);
				values2.put(ProbeDataColumns.TIMESTAMP, timeStamp.getTime());
				values2.put(ProbeDataColumns.DATUM_TYPE, 0); // 1=probe, 0=outlet
				Uri probeUri = Uri.withAppendedPath(controllerUri, Probes.TWIG_PROBES);
				Uri probeXUri = Uri.withAppendedPath(probeUri, parentRecord.toString());
				Uri insertProbeDataUri = Uri.withAppendedPath(probeXUri, ProbeData.TWIG_PROBE_DATA);
				resolver.insert(insertProbeDataUri, values2);

				/////////////////////////////////
				currentName="";
			}
			this.in_state_tag = false;
		}else if (localName.equalsIgnoreCase("name")) {
			this.in_name_tag = false;
			currentName=builder.toString().trim();
		}else if (localName.equalsIgnoreCase("probe")) {
			this.in_probe_tag = false;
		}else if (localName.equalsIgnoreCase("outlet")) {
			this.in_outlet_tag = false;
		}else if (localName.equalsIgnoreCase("probes")) {
			this.in_probes_tag = false;
		}else if (localName.equalsIgnoreCase("outlets")) {
			this.in_outlets_tag = false;
		}else if (localName.equalsIgnoreCase("serial")) {
			//	<serial>AC4:01405</serial>
			deviceType=currentName=builder.toString().trim().substring(0, 3);
			
			ContentResolver resolver = context.getContentResolver();
			ContentValues values = new ContentValues();
	        values.clear();
			values.put(ControllersColumns.DEVICE_TYPE, deviceType);
			try {
				resolver.update(controllerUri, values, null, null);
			} catch (SQLException e) {
				Log.e(LOG_TAG, "Updating controller type", e);
			}

			this.in_serial_tag = false;
		}else if (localName.equalsIgnoreCase("date")) {
			// <date>04/13/2010 09:43:44</date>
			SimpleDateFormat dateFormater = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
			try {
				timeStamp = dateFormater.parse(builder.toString().trim()); 
			} catch (java.text.ParseException e) {
				Log.e("XML Parser", "Couldn't parse the returned date field", e);
				timeStamp.setTime(0);
			}
			
			// Update the database with the new timestamp
			ContentResolver resolver = context.getContentResolver();
			ContentValues values = new ContentValues();
	        values.clear();
			values.put(ControllersColumns.LAST_UPDATED, timeStamp.getTime());
			try {
				resolver.update(controllerUri, values, null, null);
			} catch (SQLException e) {
				Log.e(LOG_TAG, "Updating controller timestamp", e);
			}

			this.in_date_tag = false;
		}

		builder.setLength(0);    
	}

	/** Gets be called on the following structure:
	 * <tag>characters</tag> */
	@Override
	public void characters(char[] ch, int start, int length)
	throws SAXException {
		super.characters(ch, start, length);
		builder.append(ch, start, length);
	}
}

There is a similar XML helper class for parsing the DataLog stream. PM me if you want that.
 
Here is the code to push changes to the Apex controller:
Code:
/**
 * pushPostChanges()
 * 		This class will spin through the list of outlets and and push all of the settings (on/off/auto)
 * 		to the controller.  I could probably make this more selective and only do the ones that have
 * 		changed, but to be honest, it was just easier to push them all.
 * 
 * @param activityContext - Activity object (scope) that called this push().  The activity is used to 
 * 							access certain graphical elements and system resources.
 * 
 * @param controllerUri - Database URI which points to the controller instance we'd like to work with.
 *
 * @throws ApexCommException
 */
	public static void pushPostChanges(Context activityContext, Uri controllerUri) throws ApexCommException {

		ContentResolver resolver = activityContext.getContentResolver();
		Cursor cursor = null;

		// First we need to get the controller specific data such as credentials and URL from the database
		String username = null;
		String password = null;
		String apexBaseURL = null;
		String controllerType = null;
		try {
			cursor = resolver.query(controllerUri, PROJECTION_CONTROLLERS, null, null, null);
			if (cursor != null && cursor.moveToFirst()) {
				// Get the URL and credentials from the preferences
				username = cursor.getString(COL_CNT_USERNAME);
				password = cursor.getString(COL_CNT_PASSWORD);
				apexBaseURL = cursor.getString(COL_CNT_URL);
				controllerType = cursor.getString(COL_CNT_DEVTYPE);
			}
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		} // end of controller query

		// Open a socket connection to the controller so that we can push the update
		if(!((username == null) || (password == null) || (apexBaseURL == null))) {
			try {
				// for this function we need to append to the URL.  I should really
				// check if the "/" was put on the end by the user here to avoid 
				// possible errors.
				if(!apexBaseURL.endsWith("/")) {
					String tmp = apexBaseURL + "/";
					apexBaseURL = tmp;
				}

				// oh, we should also check if it starts with an "http://"
				if(!apexBaseURL.startsWith("http://")) {
					String tmp = "http://" + apexBaseURL;
					apexBaseURL = tmp;
				}

				// This used to be common for both the Apex and ACiii but during
				// the 4.04 beta Apex release it seemed to have broke and forced
				// me to use status.sht for the Apex.  Maybe it was fixed but I 
				// haven't checked it.
				String apexURL;
//				if(controllerType.equalsIgnoreCase("AC4")) {
//					apexURL = apexBaseURL + "status.sht";
//				} else {
					apexURL = apexBaseURL + "cgi-bin/status.cgi";
//				}

				//Create credentials for basic auth
				UsernamePasswordCredentials c = new UsernamePasswordCredentials(username,password);

				//create a basic credentials provider and pass the credentials
				BasicCredentialsProvider cP = new BasicCredentialsProvider();
				cP.setCredentials(AuthScope.ANY, c );

				//Set credentials provider for our default http client so it will use those credentials
				// Create defaulthttpclient for communications with the Apex
				DefaultHttpClient http = new DefaultHttpClient();
				http.setCredentialsProvider(cP);

				// Build the POST update which looks like this:
				// form="status"
				// method="post"
				// action="status.sht"
				//
				// name="T5s_state", value="0"   (0=Auto, 1=Man Off, 2=Man On)
				// submit -> name="Update", value="Update"
				// -- or
				// name="FeedSel", value="0"   (0=A, 1=B)
				// submit -> name="FeedCycle", value="Feed"
				// -- or
				// submit -> name="FeedCycle", value="Feed Cancel"

				HttpPost httppost = new HttpPost(apexURL);
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); 

				// Add your data  
				nameValuePairs.add(new BasicNameValuePair("name","status"));  
				nameValuePairs.add(new BasicNameValuePair("method","post"));  
//				if(controllerType.equalsIgnoreCase("AC4")) {
//					nameValuePairs.add(new BasicNameValuePair("action","status.sht"));  
//				} else {
					nameValuePairs.add(new BasicNameValuePair("action","/cgi-bin/status.cgi"));  
//				}

				Spinner outletSpinner;
				Uri outletsUri = Uri.withAppendedPath(controllerUri, Probes.TWIG_OUTLETS);

				// Get the outlets list and see if any of the spinners have changed.
				// This could cause problems if an outlet is removed and no longer active.
				try {
					cursor = resolver.query(outletsUri, PROJECTION_PROBES, null, null, null);
					if (cursor != null && cursor.moveToFirst()) {
						do {
							int resourceId = cursor.getInt(COL_PRB_RESOURCE);
							String outletName = cursor.getString(COL_PRB_NAME);
							outletSpinner = (Spinner) ((ApexActivity) activityContext).findViewById(resourceId);
							int pendingState = outletSpinner.getSelectedItemPosition();
							String pendingStateS = String.valueOf(pendingState);
							nameValuePairs.add(new BasicNameValuePair(outletName+"_state", pendingStateS));  
						} while (cursor.moveToNext());
					}
				} catch (SQLException e) {
					Log.e(LOG_TAG, "pushUpdateToApex: getting outlets.", e);	
				} finally {
					if (cursor != null) {
						cursor.close();
					}
				} // end of controller query

				nameValuePairs.add(new BasicNameValuePair("Update","Update"));  

				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));  

				HttpResponse httpRes = http.execute(httppost);

				// Check if server response is valid
				StatusLine status = httpRes.getStatusLine();
				if (status.getStatusCode() != HTTP_STATUS_OK) {
					throw new ApexCommException("HTTP connection failed.");
				}

			} catch (ClientProtocolException e) {  
				Log.e("Apex", "HTTP-POST failed", e);
				throw new ApexCommException("HTTP POST failed.");
			} catch (IOException e) {  
				Log.e("Apex", "HTTP-POST failed", e);
				throw new ApexCommException("HTTP POST failed.");
			} 
		} // end of if(credentials)
		return;
	}

Here is how to activate the feed cycle:
Code:
	public static void activateFeedCycle(Context activityContext, Uri controllerUri) throws ApexCommException {
		ContentResolver resolver = activityContext.getContentResolver();
		Cursor cursor = null;

		String username = null;
		String password = null;
		String apexBaseURL = null;

		// First we need to get the controller specific data such as credentials and URL
		try {
			cursor = resolver.query(controllerUri, PROJECTION_CONTROLLERS, null, null, null);
			if (cursor != null && cursor.moveToFirst()) {
				// Get the URL and credentials from the preferences
				username = cursor.getString(COL_CNT_USERNAME);
				password = cursor.getString(COL_CNT_PASSWORD);
				apexBaseURL = cursor.getString(COL_CNT_URL);
			}
		} catch (SQLException e) {
			Log.e(LOG_TAG, "activateFeedCycle: getting controller facts.", e);	
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		} // end of controller query

		if(!((username == null) || (password == null) || (apexBaseURL == null))) {
			try {
				// for this function we need to append to the URL.  I should really
				// check if the "/" was put on the end by the user here to avoid 
				// possible errors.
				if(!apexBaseURL.endsWith("/")) {
					String tmp = apexBaseURL + "/";
					apexBaseURL = tmp;
				}

				// oh, we should also check if it starts with an "http://"
				if(!apexBaseURL.startsWith("http://")) {
					String tmp = "http://" + apexBaseURL;
					apexBaseURL = tmp;
				}

				String apexURL = apexBaseURL + "cgi-bin/status.xml";
				//Create credentials for basic auth
				UsernamePasswordCredentials c = new UsernamePasswordCredentials(username,password);

				//create a basic credentials provider and pass the credentials
				BasicCredentialsProvider cP = new BasicCredentialsProvider();
				cP.setCredentials(AuthScope.ANY, c );

				//Set credentials provider for our default http client so it will use those credentials
				// Create defaulthttpclient for communications with the Apex
				DefaultHttpClient http = new DefaultHttpClient();
				http.setCredentialsProvider(cP);

				// Build the POST update
				// form="status"
				// method="post"
				// action="status.sht"
				//
				// name="T5s_state", value="0"   (0=Auto, 1=Man Off, 2=Man On)
				// submit -> name="Update", value="Update"
				// -- or
				// name="FeedSel", value="0"   (0=A, 1=B)
				// submit -> name="FeedCycle", value="Feed"
				// -- or
				// submit -> name="FeedCycle", value="Feed Cancel"

				HttpPost httppost = new HttpPost(apexURL);
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); 

				// Add your data  
				// This is for the whole form:
				nameValuePairs.add(new BasicNameValuePair("name","status"));  
				nameValuePairs.add(new BasicNameValuePair("method","post"));  
				nameValuePairs.add(new BasicNameValuePair("action","/cgi-bin/status.cgi"));  

				// This is specific to the action buttons:
				Spinner feedSpinner = (Spinner) ((ApexActivity) activityContext).findViewById(300);;
				int cycleNumber = feedSpinner.getSelectedItemPosition();
				String cycleNumberString = Integer.toString(cycleNumber).trim();

				if(cycleNumber<4){
					nameValuePairs.add(new BasicNameValuePair("FeedSel", cycleNumberString));  
					nameValuePairs.add(new BasicNameValuePair("FeedCycle","Feed"));  
				} else {
					nameValuePairs.add(new BasicNameValuePair("FeedCycle","Feed Cancel"));  					
				}

				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));  

				HttpResponse httpRes = http.execute(httppost);

				// Check if server response is valid
				StatusLine status = httpRes.getStatusLine();
				if (status.getStatusCode() != HTTP_STATUS_OK) {
					throw new ApexCommException("HTTP connection failed.");
				}

			} catch (ClientProtocolException e) {  
				Log.e("Apex", "HTTP-POST failed", e);
				throw new ApexCommException("HTTP POST failed.");
			} catch (IOException e) {  
				Log.e("Apex", "HTTP-POST failed", e);
				throw new ApexCommException("HTTP POST failed.");
			} 
		} // end of if(credentials)
		return;
	}
 
Back
Top