Connecting to the Polar Personal Trainer Site
A number of people have requested access to the comms code that I use in the Polar HRM Uploader tool I created. Because of this, I've decided to write about the software here and offer it free for non-commercial use. It's not a complete implementation, you will still need to write your own HRM readers and convert that to the Polar format, but the attached code should give you a good starting point.
The communications protocol has been reversed engineered by intercepting the communications between Polar's WebLink and the Polar Personal Trainer Site. I used an intercepting proxy, called BurpProxy, that does a great job of displaying the content of HTTP traffic. Luckily, the Polar protocol is in plain text so it makes it relatively straightforward to determine what is required.
Click here to download all the source files in one archive. The code is also on GitHub
Click here to download all the source files in one archive. The code is also on GitHub
The Communications Protocol
Uploading to the Polar Site is a three stage process:
- Login. This stage requires that you authenticate with the Polar site using a basic-auth method. You can see an example of the login handshake here.
- Upload the timestamps of HRM files. The Polar site will then validate the files and return a response which indicates which files have already been uploaded. You can see an example of the timestamp handshake here.
- Upload the files. The files are uploaded using a simple XML format. You can see an example of uploading files here.
XML Marshalling
Since we need to convert from Java to XML, we use an XML data binding library called Castor. Java has come along way since I originally wrote this code and you can now use Java's JAXB reference implementation to do something similar.
There's a number of good examples on the Castor site that shows how to use the libraries.
Click the link to get the Castor mapping file that I've used.
The code below shows a snippet of the XML format used by Polar.
The code below shows a snippet of the XML format used by Polar.
<wristunit-data>
<prop name="subModel">166</prop>
<prop name="version">2</prop>
<object name="userSettings">
<prop name="maxhr">186</prop>
<prop name="resthr">50</prop>
</object>
<collection name="Exercises">
<item type="Exercise">
<prop name="origin">1</prop>
<prop name="time">2011-08-22 17:39:52.000</prop>
<prop name="save">True</prop>
<object name="mode">
<prop name="speed">True</prop>
<prop name="cadence">True</prop>
</object>
...
We represent each element with a Java object:
- Property. These are the core elements on the Polar message that describe a name and value. They have an optional type.
- Item. An Item is a collection of Polar objects and it has a Type and an optional Index.
- Collection. A named list of Items, e.g. exercises.
- Polar Object. A named collection of Properties.
- Wrist Unit Data. A wrapper object for the message.
- Webservice. A wrapper object for the Polar response.
You can download these files by clicking on the links or view them on GitHub.
Communications Code
The communications code is based upon standard Java HTTPUrlConnection and is contained in the class PolarSiteConnection. This contains simple methods for connecting to the Polar site, sending data and parsing the response.
It encapsulates the Castor data binding too, which is adequate for my project but not generic enough as there's no easy means of implementing an alternative library without some refactoring of the code.
Here's a simple example of how to use the code to login:
private void processLogin() {
// Update the status on the main dialog.
controlledFrame.setStatusMessage("Logging into Polar website...");
// Get the dialog credentials and try to connect.
String username = loginDialog.getUsername();
String password = loginDialog.getPassword();
System.out.println("Site connection is: " + siteConn);
// Pass these to the connection class.
****
Webservice ws = siteConn.intiateConnection(username, password);
****
if (ws == null) {
controlledFrame.setStatusMessage("Cannot connect to Polar site");
} else {
Status s = ws.getStatus();
if (s.isOK()) {
userPreferences = ws.getUserPreferences();
controlledFrame
.setStatusMessage("Logged into Polar website as user "
+ userPreferences.getFirstName() + " "
+ userPreferences.getLastName());
setAppState(AppState.LOGGED_IN);
} else {
controlledFrame
.setStatusMessage("Cannot log into the Polar website.");
}
}
}
Note that the variable siteConn is an instance of PolarSiteConnection. The lines wrapped in **** are the interesting bits that send data to Polar.
PolarSiteConnection also uses a library to convert the password to Base64 as part of the Basic-Auth authentication. You'll need to source your own copy of this as the licensing around the one I use prevents me from distributing it.
PolarSiteConnection also uses a library to convert the password to Base64 as part of the Basic-Auth authentication. You'll need to source your own copy of this as the licensing around the one I use prevents me from distributing it.