package org.ulti.dev.powermeter;
import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.output.XMLOutputter;
import org.ulti.dev.powermeter.parse.EventParser;
import org.ulti.dev.powermeter.util.Client;
import org.ulti.dev.powermeter.util.RFC3339DateFormat;
public class PowerMeterActions {
static public enum VariableType {
CUMULATIVE, DURATIONAL;
}
private final static String BASE_URL = "https://www.google.com/powermeter/feeds/";
private String _authToken = null;
PowerMeterConfig _config = new PowerMeterConfig();
private Logger _log = Logger.getLogger(this.getClass());
private String _userId = null;
private String _variable = null;
public PowerMeterActions() {
_authToken = _config.getToken(); // getProperties().getProperty("token");
_userId = _config.getUser();// getProperties().getProperty("user");
_variable = _config.getVariable(); // getProperties().getProperty("variable");
}
public void createNewVariable(String varId, String description, String title, String location, VariableType varKind) {
String url = makeUserUrl() + "/variable";
_log.info("URL:" + url);
Client http = new Client();
http.addHeader("Authorization", "AuthSub token=\"" + _authToken + "\"");
http.setContentType("application/atom+xml");
String uploadString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
+ " <entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:meter=\"http://schemas.google.com/meter/2008\">" + " <meter:variableId>"
+ varId + "</meter:variableId>" + " <title>" + title + "</title>" + " <content type=\"text\">" + description + "</content>"
+ " <meter:location>" + location + "</meter:location> " + " <meter:type>electricity_consumption</meter:type>"
+ " <meter:unit>kW h</meter:unit>";
if (varKind.name().equals(VariableType.CUMULATIVE.name())) {
uploadString += "<meter:cumulative/>";
} else if (varKind.name().equals(VariableType.DURATIONAL.name())) {
uploadString += "<meter:durational/>";
} else {
throw new IllegalArgumentException("Unknown kind: " + varKind);
}
uploadString += "</entry>";
String res = http.postToString(url, uploadString);
if (http.getResult() < 200 || http.getResult() > 210) {
_log.error("Error POST: code=" + http.getResult() + "\n" + res);
_log.error("POST error");
_log.error("url=" + url);
_log.error("data:\n" + uploadString);
} else {
_log.info("POST Success; you might want to check the results to be certain though.");
}
System.out.println("res: " + res);
}
/**
* Get a list of the variable for this account from the server.
*/
public void getList() {
_log.info("PowerMeterActions::getList()");
Client http = new Client();
String url = makeUserUrl() + "/variable";
_log.info("URL:" + url);
http.addHeader("Authorization", "AuthSub token=\"" + _authToken + "\"");
http.setContentType("application/atom+xml");
String res = http.getToString(url);
if (http.getResult() < 200 || http.getResult() > 210) {
System.err.println("ERROR POST: code=" + http.getResult() + "\n" + res);
_log.error("POST request error !");
_log.error("url=" + url);
}
System.out.println("result: " + res);
// List<String> variableIds=Regex.regexFindAll( res, "<meter:variableId>([^<]*)</meter:variableId>");
// for( String varId : variableIds ) {
// log.info( "variable: '"+varId+"'");
// }
}
private String makeUserUrl() {
return BASE_URL + "user/" + _userId + "/" + _userId;
}
private boolean post(String url, String data) {
Client http = new Client();
_log.info("PowerMeterActions::post( " + url + ", ... )");
http.addHeader("Authorization", "AuthSub token=\"" + _authToken + "\"");
http.setContentType("application/atom+xml");
_log.trace("POSTing data: " + data);
Document res = http.postToXML(url, data);
if (http.getResult() < 200 || http.getResult() > 210) {
_log.error("POST Error - code=" + http.getResult() + ", " + res);
_log.error("POST error");
_log.error("url=" + url);
_log.error("data:\n" + data);
return false;
} else {
String titleText = "";
boolean hasError = true;
try {
// Check for an error message; may fire an exception looking for the error title (if the document
// doesn't have an error)
Namespace atomNS = res.getRootElement().getNamespace();
Element entryElement = res.getRootElement().getChild("entry", res.getRootElement().getNamespace());
Element root = res.getRootElement();
titleText = entryElement.getChild("title", atomNS).getText();
if ("Error".equals(titleText)) {
XMLOutputter xmlo = new XMLOutputter();
_log.error("POST was successful; ERROR returned from Google PowerMeter: " + xmlo.outputString(res));
return false;
}
} catch (Exception e) {
// exception probably means error parsing for error (aka no error found)
hasError = false;
}
if (!hasError) {
_log.info("POST Success");
return true;
}
}
_log.debug("POST result: " + res);
return true;
}
/**
* Uploads a set of power events to the server 1000 at a time.
*
* @param events
*/
public void uploadDurationData(Vector<PowerEvent> events) {
_log.info("PowerMeterActions::uploadDurationData( .. )");
String preamble = "<feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:meter=\"http://schemas.google.com/meter/2008\" xmlns:batch=\"http://schemas.google.com/gdata/batch\">";
String post = "</feed>";
String uploadString = "";
// uploadString += preamble;
// https://www.google.com/powermeter/feeds/user/01266859867152863645/01266859867152863645/variable/currentcost.envi.MyEnvi.d1
String subject = makeUserUrl() + "/variable/" + _variable;
String url = BASE_URL + "event";
RFC3339DateFormat idf = new RFC3339DateFormat();
int counter = 0;
for (PowerEvent event : events) {
String eventString = "";
Namespace meterNS = Namespace.getNamespace("meter", "http://schemas.google.com/meter/2008");
Element meterElement = new Element("entry");
meterElement.addNamespaceDeclaration(meterNS);
Element categoryElement = new Element("category");
categoryElement.setAttribute("scheme", "http://schemas.google.com/g/2005#kind");
categoryElement.setAttribute("term", "http://schemas.google.com/meter/2008#durMeasurement");
Element subjectElement = new Element("subject", meterNS);
subjectElement.setText(subject);
Element startElement = new Element("startTime", meterNS);
startElement.setAttribute("uncertainty", "1.0");
startElement.setText(idf.format(event.getStart()));
Element endElement = new Element("endTime", meterNS);
endElement.setAttribute("uncertainty", "1.0");
endElement.setText(idf.format(event.getEnd()));
Element quantityElement = new Element("quantity", meterNS);
quantityElement.setAttribute("uncertainty", "0.001");
quantityElement.setAttribute("unit", "kW h");
quantityElement.setText(event.getQuantity() + "");
meterElement.addContent(categoryElement);
meterElement.addContent(subjectElement);
meterElement.addContent(startElement);
meterElement.addContent(endElement);
meterElement.addContent(quantityElement);
XMLOutputter xmlo = new XMLOutputter();
eventString = xmlo.outputString(meterElement);
uploadString += eventString;
counter++;
if (counter >= 500) {
uploadString = preamble + uploadString;
uploadString += post;
boolean success = post(url, uploadString);
if (success) {
// _config.setLastUploadDate(event.getStart());
if (events.lastElement().getStart().after(_newestUpload)) {
_newestUpload = events.lastElement().getStart();
}
}
uploadString = "";
counter = 0;
}
}
if (events.size() > 0 && uploadString.length() > 0) {
uploadString += post;
boolean success = post(url, uploadString);
if (success) {
if (events.lastElement().getStart().after(_newestUpload)) {
_newestUpload = events.lastElement().getStart();
}
}
}
_log.info("PowerMeterActions::uploadDurrationalData( .. ) - complete.");
}
@SuppressWarnings("deprecation")
private Date _newestUpload = new Date(0, 0, 0);
public void uploadInstanceData(Vector<PowerEvent> events) {
_log.info("PowerMeterActions::uploadInstanceData( .. )");
String preamble = "<feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:meter=\"http://schemas.google.com/meter/2008\" xmlns:batch=\"http://schemas.google.com/gdata/batch\">";
String post = "</feed>";
String url = BASE_URL + "event";
String uploadString = "";
String subject = makeUserUrl() + "/variable/" + _variable;
int counter = 0;
for (PowerEvent event : events) {
String eventString = "";
Namespace meterNS = Namespace.getNamespace("meter", "http://schemas.google.com/meter/2008");
Element meterElement = new Element("entry");
meterElement.addNamespaceDeclaration(meterNS);
Element categoryElement = new Element("category");
categoryElement.setAttribute("scheme", "http://schemas.google.com/g/2005#kind");
categoryElement.setAttribute("term", "http://schemas.google.com/meter/2008#instMeasurement");
Element subjectElement = new Element("subject", meterNS);
subjectElement.setText(subject);
Element occurElement = new Element("occurTime", meterNS);
occurElement.setAttribute("uncertainty", "1.0");
occurElement.setText(new RFC3339DateFormat().format(event.getStart()));
Element quantityElement = new Element("quantity", meterNS);
quantityElement.setAttribute("uncertainty", "0.001");
quantityElement.setAttribute("unit", "kW h");
quantityElement.setText(event.getQuantity() + "");
meterElement.addContent(categoryElement);
meterElement.addContent(subjectElement);
meterElement.addContent(occurElement);
meterElement.addContent(quantityElement);
XMLOutputter xmlo = new XMLOutputter();
eventString = xmlo.outputString(meterElement);
uploadString += eventString;
counter++;
if (counter >= 500) {
uploadString = preamble + uploadString;
uploadString += post;
boolean success = post(url, uploadString);
if (success) {
// _config.setLastUploadDate(event.getStart());
if (events.lastElement().getStart().after(_newestUpload)) {
_newestUpload = events.lastElement().getStart();
}
}
uploadString = "";
counter = 0;
}
}
if (events.size() > 0 && uploadString.length() > 0) {
uploadString += post;
boolean success = post(url, uploadString);
if (success) {
if (events.lastElement().getStart().after(_newestUpload)) {
_newestUpload = events.lastElement().getStart();
}
}
}
_log.info("PowerMeterActions::uploadInstanceData( .. ) - complete.");
}
public void saveConfig() {
_config.setLastUploadDate(_newestUpload);
_config.saveConfig();
}
PowerMeterConfig getConfig() {
return _config;
}
Vector<PowerEvent> parse(String fName) {
_log.info("PowerMeterActions::parse( " + fName + " )");
if (!new File(fName).exists()) {
throw new RuntimeException("Cannot parse: " + new File(fName).getAbsolutePath() + ", file does not exist");
}
String parserName = _config.getParser();
EventParser parser = null;
try {
@SuppressWarnings("unchecked")
Class<EventParser> classDefinition = (Class<EventParser>) Class.forName(parserName);
parser = classDefinition.newInstance();
} catch (ClassCastException cce) {
_log.error(cce);
} catch (InstantiationException ie) {
_log.error(ie);
} catch (IllegalAccessException iae) {
_log.error(iae);
} catch (ClassNotFoundException cnfe) {
_log.error(cnfe);
}
if (parser == null) {
String msg = "Could not instantiate parser; please add eventParser property to config.properties";
_log.fatal(msg);
throw new RuntimeException(msg);
}
Vector<PowerEvent> events = parser.read(fName);
Collections.sort(events, new Comparator<PowerEvent>() {
@Override
public int compare(PowerEvent arg0, PowerEvent arg1) {
return arg0.getStart().compareTo(arg1.getStart());
}
});
// purge events listed before the last upload
Date lastUpload = _config.getLastUploadDate();
_log.info("# events: " + events.size() + " lastUpload date: " + lastUpload);
Iterator<PowerEvent> eventIterator = events.iterator();
while (eventIterator.hasNext()) {
PowerEvent event = eventIterator.next();
_log.trace(event.toString());
if (event.getStart().before(lastUpload)) {
eventIterator.remove();
}
}
_log.info("# events after pruning with last upload date: " + events.size());
return events;
}
}