Skip to content
Snippets Groups Projects
Commit c3103400 authored by Lennart Goedhart's avatar Lennart Goedhart Committed by GitHub
Browse files

Merge pull request #105 from jgwashburn/retrofit

Access Nightscout API via Retrofit
parents 9600f5b4 92222bc8
No related branches found
No related tags found
No related merge requests found
Showing
with 579 additions and 239 deletions
...@@ -7,6 +7,7 @@ buildscript { ...@@ -7,6 +7,7 @@ buildscript {
dependencies { dependencies {
classpath 'io.fabric.tools:gradle:1.21.6' classpath 'io.fabric.tools:gradle:1.21.6'
classpath 'io.realm:realm-gradle-plugin:2.3.1'
classpath 'org.ajoberstar:grgit:1.5.0' classpath 'org.ajoberstar:grgit:1.5.0'
} }
} }
...@@ -154,11 +155,16 @@ dependencies { ...@@ -154,11 +155,16 @@ dependencies {
compile 'org.apache.commons:commons-lang3:3.4' compile 'org.apache.commons:commons-lang3:3.4'
compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar' compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar'
compile 'uk.co.chrisjenx:calligraphy:2.2.0' compile 'uk.co.chrisjenx:calligraphy:2.2.0'
compile 'com.bugfender.sdk:android:0.6.2' compile 'com.bugfender.sdk:android:0.7.2'
compile 'com.jjoe64:graphview:4.2.1' compile 'com.jjoe64:graphview:4.2.1'
compile 'com.android.support:support-v4:23.4.0' compile 'com.android.support:support-v4:23.4.0'
compile 'com.google.code.gson:gson:2.7' compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
compile 'com.google.android.gms:play-services-appindexing:8.4.0' compile 'com.google.android.gms:play-services-appindexing:8.4.0'
} }
\ No newline at end of file
...@@ -82,6 +82,11 @@ ...@@ -82,6 +82,11 @@
<receiver android:name=".upload.nightscout.NightscoutUploadReceiver" /> <receiver android:name=".upload.nightscout.NightscoutUploadReceiver" />
<receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" /> <receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" />
<receiver android:name=".medtronic.service.MedtronicCnlAlarmReceiver"></receiver>
</application> </application>
</manifest> </manifest>
\ No newline at end of file
...@@ -81,6 +81,10 @@ public class PumpStatusEvent extends RealmObject { ...@@ -81,6 +81,10 @@ public class PumpStatusEvent extends RealmObject {
return CGM_TREND.valueOf(cgmTrend); return CGM_TREND.valueOf(cgmTrend);
} }
public String getCgmTrendString() {
return cgmTrend;
}
public void setCgmTrend(CGM_TREND cgmTrend) { public void setCgmTrend(CGM_TREND cgmTrend) {
if (cgmTrend != null) if (cgmTrend != null)
this.cgmTrend = cgmTrend.name(); this.cgmTrend = cgmTrend.name();
......
package info.nightscout.android.upload.nightscout;
import android.util.Log;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
import info.nightscout.android.upload.nightscout.serializer.EntriesSerializer;
import android.support.annotation.NonNull;
import info.nightscout.api.UploadApi;
import info.nightscout.api.GlucoseEndpoints;
import info.nightscout.api.BolusEndpoints.BolusEntry;
import info.nightscout.api.GlucoseEndpoints.GlucoseEntry;
import info.nightscout.api.BolusEndpoints;
import info.nightscout.api.DeviceEndpoints;
import info.nightscout.api.DeviceEndpoints.Iob;
import info.nightscout.api.DeviceEndpoints.Battery;
import info.nightscout.api.DeviceEndpoints.PumpStatus;
import info.nightscout.api.DeviceEndpoints.PumpInfo;
import info.nightscout.api.DeviceEndpoints.DeviceStatus;
public class NightScoutUpload {
private static final String TAG = NightscoutUploadIntentService.class.getSimpleName();
private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
public NightScoutUpload () {
}
public Boolean doRESTUpload(String url,
String secret,
int uploaderBatteryLevel,
List<PumpStatusEvent> records) {
Boolean success = false;
try {
success = isUploaded(records, url, secret, uploaderBatteryLevel);
} catch (Exception e) {
Log.e(TAG, "Unable to do REST API Upload to: " + url, e);
}
return success;
}
private boolean isUploaded(List<PumpStatusEvent> records,
String baseURL,
String secret,
int uploaderBatteryLevel) throws Exception {
UploadApi uploadApi = new UploadApi(baseURL, formToken(secret));
boolean eventsUploaded = uploadEvents(uploadApi.getGlucoseEndpoints(),
uploadApi.getBolusApi(),
records );
boolean deviceStatusUploaded = uploadDeviceStatus(uploadApi.getDeviceEndpoints(),
uploaderBatteryLevel, records);
return eventsUploaded && deviceStatusUploaded;
}
private boolean uploadEvents(GlucoseEndpoints glucoseEndpoints,
BolusEndpoints bolusEndpoints,
List<PumpStatusEvent> records ) throws Exception {
List<GlucoseEntry> glucoseEntries = new ArrayList<>();
List<BolusEntry> bolusEntries = new ArrayList<>();
for (PumpStatusEvent record : records) {
GlucoseEntry glucoseEntry = new GlucoseEntry();
glucoseEntry.setType("sgv");
glucoseEntry.setDirection(EntriesSerializer.getDirectionStringStatus(record.getCgmTrend()));
glucoseEntry.setDevice(record.getDeviceName());
glucoseEntry.setSgv(record.getSgv());
glucoseEntry.setDate(record.getEventDate().getTime());
glucoseEntry.setDateString(record.getEventDate().toString());
glucoseEntries.add(glucoseEntry);
BolusEntry bolusEntry = new BolusEntry();
bolusEntry.setType("mbg");
bolusEntry.setDate(record.getEventDate().getTime());
bolusEntry.setDateString(record.getEventDate().toString());
bolusEntry.setDevice(record.getDeviceName());
bolusEntry.setMbg(record.getBolusWizardBGL());
bolusEntries.add(bolusEntry);
}
glucoseEndpoints.sendEntries(glucoseEntries).execute();
bolusEndpoints.sendEntries(bolusEntries).execute();
return true;
}
private boolean uploadDeviceStatus(DeviceEndpoints deviceEndpoints,
int uploaderBatteryLevel,
List<PumpStatusEvent> records) throws Exception {
List<DeviceStatus> deviceEntries = new ArrayList<>();
for (PumpStatusEvent record : records) {
Iob iob = new Iob(record.getPumpDate(), record.getActiveInsulin());
Battery battery = new Battery(record.getBatteryPercentage());
PumpStatus pumpstatus;
if (record.isBolusing()) {
pumpstatus = new PumpStatus(true, false, "");
} else if (record.isSuspended()) {
pumpstatus = new PumpStatus(false, true, "");
} else {
pumpstatus = new PumpStatus(false, false, "normal");
}
PumpInfo pumpInfo = new PumpInfo(
ISO8601_DATE_FORMAT.format(record.getPumpDate()),
new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP),
iob,
battery,
pumpstatus
);
DeviceStatus deviceStatus = new DeviceStatus(
uploaderBatteryLevel,
record.getDeviceName(),
ISO8601_DATE_FORMAT.format(record.getPumpDate()),
pumpInfo
);
deviceEntries.add(deviceStatus);
}
for (DeviceStatus status: deviceEntries) {
deviceEndpoints.sendDeviceStatus(status).execute();
}
return true ;
}
@NonNull
private String formToken(String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] bytes = secret.getBytes("UTF-8");
digest.update(bytes, 0, bytes.length);
bytes = digest.digest();
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
}
...@@ -2,7 +2,6 @@ package info.nightscout.android.upload.nightscout; ...@@ -2,7 +2,6 @@ package info.nightscout.android.upload.nightscout;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
/** /**
* Created by lgoedhart on 26/06/2016. * Created by lgoedhart on 26/06/2016.
*/ */
......
...@@ -10,40 +10,19 @@ import android.preference.PreferenceManager; ...@@ -10,40 +10,19 @@ import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.util.Log; import android.util.Log;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONObject;
import java.math.BigDecimal;
import java.net.URL;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import info.nightscout.android.R; import info.nightscout.android.R;
import info.nightscout.android.medtronic.MainActivity; import info.nightscout.android.medtronic.MainActivity;
import info.nightscout.android.model.medtronicNg.PumpStatusEvent; import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
import info.nightscout.android.upload.nightscout.serializer.EntriesSerializer;
import io.realm.Realm; import io.realm.Realm;
import io.realm.RealmResults; import io.realm.RealmResults;
public class NightscoutUploadIntentService extends IntentService { public class NightscoutUploadIntentService extends IntentService {
private static final String TAG = NightscoutUploadIntentService.class.getSimpleName(); private static final String TAG = NightscoutUploadIntentService.class.getSimpleName();
private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
private static final int SOCKET_TIMEOUT = 60 * 1000;
private static final int CONNECTION_TIMEOUT = 30 * 1000;
private Context mContext; private Context mContext;
private Realm mRealm; private Realm mRealm;
private NightScoutUpload mNightScoutUpload;
public NightscoutUploadIntentService() { public NightscoutUploadIntentService() {
super(NightscoutUploadIntentService.class.getName()); super(NightscoutUploadIntentService.class.getName());
...@@ -63,6 +42,8 @@ public class NightscoutUploadIntentService extends IntentService { ...@@ -63,6 +42,8 @@ public class NightscoutUploadIntentService extends IntentService {
Log.i(TAG, "onCreate called"); Log.i(TAG, "onCreate called");
mContext = this.getBaseContext(); mContext = this.getBaseContext();
mNightScoutUpload = new NightScoutUpload();
} }
@Override @Override
...@@ -84,7 +65,18 @@ public class NightscoutUploadIntentService extends IntentService { ...@@ -84,7 +65,18 @@ public class NightscoutUploadIntentService extends IntentService {
if (enableRESTUpload) { if (enableRESTUpload) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Log.i(TAG, String.format("Starting upload of %s record using a REST API", records.size())); Log.i(TAG, String.format("Starting upload of %s record using a REST API", records.size()));
doRESTUpload(prefs, records); String urlSetting = prefs.getString(mContext.getString(R.string.preference_nightscout_url), "");
String secretSetting = prefs.getString(mContext.getString(R.string.preference_api_secret), "YOURAPISECRET");
int uploaderBatteryLevel = MainActivity.batLevel;
Boolean uploadSuccess = mNightScoutUpload.doRESTUpload(urlSetting,
secretSetting, uploaderBatteryLevel, records);
if (uploadSuccess) {
mRealm.beginTransaction();
for (PumpStatusEvent updateRecord : records) {
updateRecord.setUploaded(true);
}
mRealm.commitTransaction();
}
Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", records.size(), System.currentTimeMillis() - start)); Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", records.size(), System.currentTimeMillis() - start));
} }
} catch (Exception e) { } catch (Exception e) {
...@@ -98,217 +90,6 @@ public class NightscoutUploadIntentService extends IntentService { ...@@ -98,217 +90,6 @@ public class NightscoutUploadIntentService extends IntentService {
NightscoutUploadReceiver.completeWakefulIntent(intent); NightscoutUploadReceiver.completeWakefulIntent(intent);
} }
private void doRESTUpload(SharedPreferences prefs, RealmResults<PumpStatusEvent> records) {
String apiScheme = "https://";
String apiUrl = "";
String apiSecret = prefs.getString(mContext.getString(R.string.preference_api_secret), "YOURAPISECRET");
// TODO - this code needs to go to the Settings Activity.
// Add the extra match for "KEY@" to support the previous single field
Pattern p = Pattern.compile("(.*\\/\\/)?(.*@)?([^\\/]*)(.*)");
Matcher m = p.matcher(prefs.getString(mContext.getString(R.string.preference_nightscout_url), ""));
if (m.find()) {
apiUrl = m.group(3);
// Only override apiSecret from URL (the "old" way), if the API secret preference is empty
if (apiSecret.equals("YOURAPISECRET") || apiSecret.equals("")) {
apiSecret = (m.group(2) == null) ? "" : m.group(2).replace("@", "");
}
// Override the URI scheme if it's been provided in the preference)
if (m.group(1) != null && !m.group(1).equals("")) {
apiScheme = m.group(1);
}
}
// Update the preferences to match what we expect. Only really used from converting from the
// old format to the new format. Aren't we nice for managing backward compatibility?
prefs.edit().putString(mContext.getString(R.string.preference_api_secret), apiSecret).apply();
prefs.edit().putString(mContext.getString(R.string.preference_nightscout_url), String.format("%s%s", apiScheme, apiUrl)).apply();
String uploadUrl = String.format("%s%s@%s/api/v1/", apiScheme, apiSecret, apiUrl);
try {
doRESTUploadTo(uploadUrl, records);
} catch (Exception e) {
Log.e(TAG, "Unable to do REST API Upload to: " + uploadUrl, e);
}
}
private void doRESTUploadTo(String baseURI, RealmResults<PumpStatusEvent> records) {
try {
String baseURL;
String secret = null;
String[] uriParts = baseURI.split("@");
if (uriParts.length == 1) {
throw new Exception("Starting with API v1, a pass phase is required");
} else if (uriParts.length == 2) {
secret = uriParts[0];
baseURL = uriParts[1];
// new format URL!
if (secret.contains("http")) {
if (secret.contains("https")) {
baseURL = "https://" + baseURL;
} else {
baseURL = "http://" + baseURL;
}
String[] uriParts2 = secret.split("//");
secret = uriParts2[1];
}
} else {
throw new Exception(String.format("Unexpected baseURI: %s, uriParts.length: %s", baseURI, uriParts.length));
}
JSONArray devicestatusBody = new JSONArray();
JSONArray entriesBody = new JSONArray();
for (PumpStatusEvent record : records) {
addDeviceStatus(devicestatusBody, record);
addSgvEntry(entriesBody, record);
addMbgEntry(entriesBody, record);
}
boolean isUploaded = uploadToNightscout(new URL(baseURL + "/entries"), secret, entriesBody);
for(int i = 0; isUploaded && i < devicestatusBody.length(); i++) {
isUploaded &= uploadToNightscout(new URL(baseURL + "/devicestatus"), secret, devicestatusBody.getJSONObject(i));
}
if (isUploaded) {
// Yay! We uploaded. Tell Realm
// FIXME - check the upload succeeded!
mRealm.beginTransaction();
for (PumpStatusEvent updateRecord : records) {
updateRecord.setUploaded(true);
}
mRealm.commitTransaction();
}
} catch (Exception e) {
Log.e(TAG, "Unable to post data", e);
}
}
private boolean uploadToNightscout(URL endpoint, String secret, JSONObject httpBody) throws Exception {
return uploadToNightscout(endpoint, secret, httpBody.toString());
}
private boolean uploadToNightscout(URL endpoint, String secret, JSONArray httpBody) throws Exception {
return uploadToNightscout(endpoint, secret, httpBody.toString());
}
private boolean uploadToNightscout(URL endpoint, String secret, String httpBody) throws Exception {
Log.i(TAG, "postURL: " + endpoint.toString());
HttpPost post = new HttpPost(endpoint.toString());
if (secret == null || secret.isEmpty()) {
throw new Exception("Starting with API v1, a pass phase is required");
} else {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] bytes = secret.getBytes("UTF-8");
digest.update(bytes, 0, bytes.length);
bytes = digest.digest();
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(String.format("%02x", b & 0xff));
}
String token = sb.toString();
post.setHeader("api-secret", token);
}
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
DefaultHttpClient httpclient = new DefaultHttpClient(params);
Log.i(TAG, "Upload JSON: " + httpBody);
try {
StringEntity se = new StringEntity(httpBody);
post.setEntity(se);
post.setHeader("Accept", "application/json");
post.setHeader("Content-type", "application/json");
ResponseHandler responseHandler = new BasicResponseHandler();
httpclient.execute(post, responseHandler);
} catch (Exception e) {
Log.w(TAG, "Unable to post data to: '" + post.getURI().toString() + "'", e);
return false;
}
return true;
}
private void addDeviceStatus(JSONArray devicestatusArray, PumpStatusEvent record) throws Exception {
JSONObject json = new JSONObject();
json.put("uploaderBattery", MainActivity.batLevel);
json.put("device", record.getDeviceName());
json.put("created_at", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
JSONObject pumpInfo = new JSONObject();
pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
pumpInfo.put("reservoir", new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP));
JSONObject iob = new JSONObject();
iob.put("timestamp", record.getPumpDate());
iob.put("bolusiob", record.getActiveInsulin());
JSONObject status = new JSONObject();
if (record.isBolusing()) {
status.put("bolusing", true);
} else if (record.isSuspended()) {
status.put("suspended", true);
} else {
status.put("status", "normal");
}
JSONObject battery = new JSONObject();
battery.put("percent", record.getBatteryPercentage());
pumpInfo.put("iob", iob);
pumpInfo.put("battery", battery);
pumpInfo.put("status", status);
json.put("pump", pumpInfo);
String jsonString = json.toString();
Log.i(TAG, "Device Status JSON: " + jsonString);
devicestatusArray.put(json);
}
private void addSgvEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception {
JSONObject json = new JSONObject();
// TODO replace with Retrofit/EntriesSerializer
json.put("sgv", pumpRecord.getSgv());
json.put("direction", EntriesSerializer.getDirectionString(pumpRecord.getCgmTrend()));
json.put("device", pumpRecord.getDeviceName());
json.put("type", "sgv");
json.put("date", pumpRecord.getEventDate().getTime());
json.put("dateString", pumpRecord.getEventDate());
entriesArray.put(json);
}
private void addMbgEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception {
if (pumpRecord.hasRecentBolusWizard()) {
JSONObject json = new JSONObject();
// TODO replace with Retrofit/EntriesSerializer
json.put("type", "mbg");
json.put("mbg", pumpRecord.getBolusWizardBGL());
json.put("device", pumpRecord.getDeviceName());
json.put("date", pumpRecord.getEventDate().getTime());
json.put("dateString", pumpRecord.getEventDate());
entriesArray.put(json);
}
}
private boolean isOnline() { private boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo(); NetworkInfo netInfo = cm.getActiveNetworkInfo();
...@@ -320,4 +101,6 @@ public class NightscoutUploadIntentService extends IntentService { ...@@ -320,4 +101,6 @@ public class NightscoutUploadIntentService extends IntentService {
public static final String EXTENDED_DATA = "info.nightscout.android.upload.nightscout.DATA"; public static final String EXTENDED_DATA = "info.nightscout.android.upload.nightscout.DATA";
} }
} }
...@@ -42,6 +42,35 @@ public class EntriesSerializer implements JsonSerializer<PumpStatusEvent> { ...@@ -42,6 +42,35 @@ public class EntriesSerializer implements JsonSerializer<PumpStatusEvent> {
} }
} }
public static String getDirectionStringStatus(PumpStatusEvent.CGM_TREND trend) {
switch( trend ) {
case NONE:
return "NONE";
case DOUBLE_UP:
return "DoubleUp";
case SINGLE_UP:
return "SingleUp";
case FOURTY_FIVE_UP:
return "FortyFiveUp";
case FLAT:
return "Flat";
case FOURTY_FIVE_DOWN:
return "FortyFiveDown";
case SINGLE_DOWN:
return "SingleDown";
case DOUBLE_DOWN:
return "DoubleDown";
case NOT_COMPUTABLE:
return "NOT COMPUTABLE";
case RATE_OUT_OF_RANGE:
return "RATE OUT OF RANGE";
case NOT_SET:
return "NONE";
default:
return "NOT COMPUTABLE"; // TODO - should this be something else?
}
}
@Override @Override
public JsonElement serialize(PumpStatusEvent src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(PumpStatusEvent src, Type typeOfSrc, JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject(); final JsonObject jsonObject = new JsonObject();
......
package info.nightscout.api;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface BolusEndpoints {
class BolusEntry {
String type;
String dateString;
float date;
float mbg;
String device;
public BolusEntry() { }
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDateString() {
return dateString;
}
public void setDateString(String dateString) {
this.dateString = dateString;
}
public float getDate() {
return date;
}
public void setDate(float date) {
this.date = date;
}
public float getMbg() {
return mbg;
}
public void setMbg(float mbg) {
this.mbg = mbg;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
}
@Headers({
"Accept: application/json",
"Content-type: application/json"
})
@POST("/api/v1/entries")
Call<ResponseBody> sendEntries(@Body List<BolusEntry> entries);
}
package info.nightscout.api;
import java.math.BigDecimal;
import java.util.Date;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface DeviceEndpoints {
class Iob {
final Date timestamp;
final float bolusiob;
public Iob (Date timestamp,
float bolusiob) {
this.timestamp = timestamp;
this.bolusiob = bolusiob;
}
}
class Battery {
final short percent;
public Battery(short percent) {
this.percent = percent;
}
}
class PumpStatus {
final Boolean bolusing;
final Boolean suspended;
final String status;
public PumpStatus(
Boolean bolusing,
Boolean suspended,
String status
) {
this.bolusing = bolusing;
this.suspended = suspended;
this.status = status;
}
}
class PumpInfo {
final String clock;
final BigDecimal reservoir;
final Iob iob;
final Battery battery;
final PumpStatus status;
public PumpInfo(String clock,
BigDecimal reservoir,
Iob iob,
Battery battery,
PumpStatus status) {
this.clock = clock;
this.reservoir = reservoir;
this.iob = iob;
this.battery = battery;
this.status = status;
}
}
class DeviceStatus {
final Integer uploaderBattery;
final String device;
final String created_at;
final PumpInfo pump;
public DeviceStatus(Integer uploaderBattery,
String device,
String created_at,
PumpInfo pump) {
this.uploaderBattery = uploaderBattery;
this.device = device;
this.created_at = created_at;
this.pump = pump;
}
}
@Headers({
"Accept: application/json",
"Content-type: application/json"
})
@POST("/api/v1/devicestatus")
Call<ResponseBody> sendDeviceStatus(@Body DeviceStatus deviceStatus);
}
package info.nightscout.api;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface GlucoseEndpoints {
class GlucoseEntry {
String type;
String dateString;
float date;
float sgv;
String direction;
String device;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDateString() {
return dateString;
}
public void setDateString(String dateString) {
this.dateString = dateString;
}
public float getDate() {
return date;
}
public void setDate(float date) {
this.date = date;
}
public float getSgv() {
return sgv;
}
public void setSgv(float sgv) {
this.sgv = sgv;
}
public String getDirection() {
return direction;
}
public void setDirection(String direction) {
this.direction = direction;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
public GlucoseEntry() { }
}
@Headers({
"Accept: application/json",
"Content-type: application/json"
})
@POST("/api/v1/entries")
Call<ResponseBody> sendEntries(@Body List<GlucoseEntry> entries);
}
package info.nightscout.api;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class UploadApi {
private Retrofit retrofit;
private GlucoseEndpoints glucoseEndpoints;
private BolusEndpoints bolusApi;
private DeviceEndpoints deviceEndpoints;
public GlucoseEndpoints getGlucoseEndpoints() {
return glucoseEndpoints;
}
public BolusEndpoints getBolusApi() {
return bolusApi;
}
public DeviceEndpoints getDeviceEndpoints() {
return deviceEndpoints;
}
public UploadApi(String baseURL, String token) {
class AddAuthHeader implements Interceptor {
private String token;
public AddAuthHeader(String token) {
this.token = token;
}
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("api-secret", token)
.method( original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
};
OkHttpClient.Builder okHttpClient = new OkHttpClient().newBuilder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS);
okHttpClient.addInterceptor(new AddAuthHeader(token));
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClient.addInterceptor(logging);
retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.client(okHttpClient.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
glucoseEndpoints = retrofit.create(GlucoseEndpoints.class);
bolusApi = retrofit.create(BolusEndpoints.class);
deviceEndpoints = retrofit.create(DeviceEndpoints.class);
}
}
...@@ -25,9 +25,11 @@ ...@@ -25,9 +25,11 @@
<string name="action_sign_in_short">Retrieve keys</string> <string name="action_sign_in_short">Retrieve keys</string>
<string name="error_invalid_password">Password is required</string> <string name="error_invalid_password">Password is required</string>
<string name="error_incorrect_password">The Username or password is incorrect</string> <string name="error_incorrect_password">The Username or password is incorrect</string>
<string name="error_client_protocol_exception">Could not communicate with server.</string>
<string name="error_io_exception">Could not connect to server.</string>
<string name="error_class_not_found_exception">Application code error.</string>
<string name="error_http_response">Server responded with error. Could be username or password problem.</string>
<string name="error_field_required">This field is required</string> <string name="error_field_required">This field is required</string>
<string name="preference_nightscout_url">Nightscout URL</string>
<string name="preference_api_secret">API SECRET</string>
<string name="prompt_carelink_username_password">Please enter your CareLink details.\nThey will not be stored.</string> <string name="prompt_carelink_username_password">Please enter your CareLink details.\nThey will not be stored.</string>
<string name="close">Close</string> <string name="close">Close</string>
<string name="register_contour_next_link">Registered Devices</string> <string name="register_contour_next_link">Registered Devices</string>
...@@ -51,6 +53,8 @@ ...@@ -51,6 +53,8 @@
<string name="dummy_content">DUMMY\nCONTENT</string> <string name="dummy_content">DUMMY\nCONTENT</string>
<string name="menu_name_status">Status</string> <string name="menu_name_status">Status</string>
<string name="menu_name_battery_status">unknown</string> <string name="menu_name_battery_status">unknown</string>
<string name="preference_api_secret">YOUR.API.SECRET</string>
<string name="preference_nightscout_url">YOUR.NIGHTSCOUT.URL</string>
<string name="preferences_poll_interval">Poll interval</string> <string name="preferences_poll_interval">Poll interval</string>
<string name="preferences_low_battery_poll_interval">Poll interval on low pump battery</string> <string name="preferences_low_battery_poll_interval">Poll interval on low pump battery</string>
...@@ -58,4 +62,5 @@ ...@@ -58,4 +62,5 @@
<string name="to_register_a_contour_next_link_you_must_first_plug_it_in_and_get_a_reading_from_the_pump">To register a Contour Next Link you must first plug it in, and get a reading from the pump.</string> <string name="to_register_a_contour_next_link_you_must_first_plug_it_in_and_get_a_reading_from_the_pump">To register a Contour Next Link you must first plug it in, and get a reading from the pump.</string>
<string name="serial_number">Serial number</string> <string name="serial_number">Serial number</string>
<string name="preferences_chart_interval">Chart Zoom</string> <string name="preferences_chart_interval">Chart Zoom</string>
</resources> </resources>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment