Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling Temp Basal events in NightscoutTreatments. #3597

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.eveningoutpost.dexdrip.cgm.nsfollow;

import com.eveningoutpost.dexdrip.BuildConfig;
import com.eveningoutpost.dexdrip.cgm.nsfollow.messages.Profile;
import com.eveningoutpost.dexdrip.models.JoH;
import com.eveningoutpost.dexdrip.models.UserError;
import com.eveningoutpost.dexdrip.utilitymodels.CollectionServiceStarter;
import com.eveningoutpost.dexdrip.utilitymodels.Constants;
import com.eveningoutpost.dexdrip.utilitymodels.NightscoutTreatments;
import com.eveningoutpost.dexdrip.utilitymodels.Pref;
import com.eveningoutpost.dexdrip.profileeditor.BasalProfile;
import com.eveningoutpost.dexdrip.utilitymodels.*;
import com.eveningoutpost.dexdrip.cgm.nsfollow.messages.Entry;
import com.eveningoutpost.dexdrip.cgm.nsfollow.utils.NightscoutUrl;
import com.eveningoutpost.dexdrip.evaluators.MissedReadingsEstimator;
Expand Down Expand Up @@ -66,6 +65,9 @@ public interface Nightscout {

@GET("/api/v1/treatments")
Call<ResponseBody> getTreatments(@Header("api-secret") String secret);

@GET("/api/v1/profile")
Call<List<Profile>> getProfiles(@Header("api-secret") String secret);
}

private static Nightscout getService() {
Expand Down Expand Up @@ -101,14 +103,36 @@ public static void work(final boolean live) {
session.treatmentsCallback = new NightscoutCallback<ResponseBody>("NS treatments download", session, () -> {
// process data
try {
NightscoutTreatments.processTreatmentResponse(session.treatments.string());
NightscoutFollowService.updateTreatmentDownloaded();
final String response = session.treatments.string();

if (treatmentDownloadEnabled()) {
NightscoutTreatments.processTreatmentResponse(response);
NightscoutFollowService.updateTreatmentDownloaded();
}

if (basalRateDownloadEnabled()) {
NightscoutBasalRate.setTreatments(response);
}
} catch (Exception e) {
msg("Treatments: " + e);
}
})
.setOnFailure(() -> msg(session.treatmentsCallback.getStatus()));

// set up processing callback for profiles
session.profilesCallback = new NightscoutCallback<List<Profile>>("NS profiles download", session, () -> {
// process data
try {
List<Double> profile = session.currentProfile.getDefaultBasalProfile();

BasalProfile.save(BasalProfile.getActiveRateName(), profile);
NightscoutBasalRate.setProfile(profile);
} catch (Exception e) {
msg("Profile: " + e);
}
})
.setOnFailure(() -> msg(session.profilesCallback.getStatus()));

if (!emptyString(urlString)) {
try {
int count = Math.min(MissedReadingsEstimator.estimate() + 1, (int) (Constants.DAY_IN_MS / DEXCOM_PERIOD));
Expand All @@ -119,7 +143,19 @@ public static void work(final boolean live) {
UserError.Log.e(TAG, "Exception in entries work() " + e);
msg("Nightscout follow entries error: " + e);
}
if (treatmentDownloadEnabled()) {

if (basalRateDownloadEnabled()) {
if (JoH.ratelimit("nsfollow-profile-download", 60)) {
try {
getService().getProfiles(session.url.getHashedSecret()).enqueue(session.profilesCallback);
} catch (Exception e) {
UserError.Log.e(TAG, "Exception in profiles work() " + e);
msg("Nightscout follow profiles error: " + e);
}
}
}

if (treatmentDownloadEnabled() || basalRateDownloadEnabled()) {
if (JoH.ratelimit("nsfollow-treatment-download", 60)) {
try {
getService().getTreatments(session.url.getHashedSecret()).enqueue(session.treatmentsCallback);
Expand All @@ -142,6 +178,10 @@ static boolean treatmentDownloadEnabled() {
return Pref.getBooleanDefaultFalse("nsfollow_download_treatments");
}

static boolean basalRateDownloadEnabled() {
return Pref.getBoolean("nsfollow_download_basalrate", true);
}

public static final TypeAdapter<Number> UNRELIABLE_INTEGER = new TypeAdapter<Number>() {
@Override
public Number read(JsonReader in) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class NightscoutFollowService extends ForegroundService {

private static final String TAG = "NightscoutFollow";
private static final long SAMPLE_PERIOD = DEXCOM_PERIOD;
private static final long FREQUENT_PERIOD = 60_000;

protected static volatile String lastState = "";

Expand All @@ -67,6 +68,10 @@ private void buggySamsungCheck() {
wakeup_time = 0;
}

static long connectPeriod() {
return Pref.getBoolean("nsfollow_download_per_minute", false) ? FREQUENT_PERIOD : SAMPLE_PERIOD;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final PowerManager.WakeLock wl = JoH.getWakeLock("NSFollow-osc", 60000);
Expand All @@ -88,7 +93,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
if (lastBg != null) {
lastBgTime = lastBg.timestamp;
}
if (lastBg == null || JoH.msSince(lastBg.timestamp) > SAMPLE_PERIOD) {
if (lastBg == null || JoH.msSince(lastBg.timestamp) > connectPeriod()) {
if (JoH.ratelimit("last-ns-follow-poll", 5)) {
Inevitable.task("NS-Follow-Work", 200, () -> {
NightscoutFollow.work(true);
Expand Down Expand Up @@ -130,7 +135,7 @@ static void scheduleWakeUp() {
final long last = lastBg != null ? lastBg.timestamp : 0;

final long grace = Constants.SECOND_IN_MS * 10;
final long next = Anticipate.next(JoH.tsl(), last, SAMPLE_PERIOD, grace) + grace;
final long next = Anticipate.next(JoH.tsl(), last, connectPeriod(), grace) + grace;
wakeup_time = next;
UserError.Log.d(TAG, "Anticipate next: " + JoH.dateTimeText(next) + " last: " + JoH.dateTimeText(last));

Expand Down Expand Up @@ -161,10 +166,10 @@ public static List<StatusItem> megaStatus() {
Highlight ageOfLastBgPollHighlight = Highlight.NORMAL;
if (bgReceiveDelay > 0) {
ageOfBgLastPoll = JoH.niceTimeScalar(bgReceiveDelay);
if (bgReceiveDelay - lag > SAMPLE_PERIOD / 2) {
if (bgReceiveDelay - lag > connectPeriod() / 2) {
ageOfLastBgPollHighlight = Highlight.BAD;
}
if (bgReceiveDelay - lag > SAMPLE_PERIOD * 2) {
if (bgReceiveDelay - lag > connectPeriod() * 2) {
ageOfLastBgPollHighlight = Highlight.CRITICAL;
}
}
Expand All @@ -175,7 +180,7 @@ public static List<StatusItem> megaStatus() {
if (lastBg != null) {
long age = JoH.msSince(lastBg.timestamp);
ageLastBg = JoH.niceTimeScalar(age);
if (age > SAMPLE_PERIOD + hightlightGrace + lag) {
if (age > connectPeriod() + hightlightGrace + lag) {
bgAgeHighlight = Highlight.BAD;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.eveningoutpost.dexdrip.cgm.nsfollow;

import com.eveningoutpost.dexdrip.cgm.nsfollow.messages.Entry;
import com.eveningoutpost.dexdrip.cgm.nsfollow.messages.Profile;
import com.eveningoutpost.dexdrip.cgm.nsfollow.utils.NightscoutUrl;

import java.util.List;
Expand All @@ -20,12 +21,14 @@ public class Session {
public NightscoutUrl url;
public BaseCallback<List<Entry>> entriesCallback;
public BaseCallback<ResponseBody> treatmentsCallback;
public BaseCallback<List<Profile>> profilesCallback;


// most recent set of entries
public List<Entry> entries;
// most recent treatments raw json
public ResponseBody treatments;
public Profile currentProfile;


// populate session data from a response object which could be any supported type
Expand All @@ -36,6 +39,11 @@ public void populate(final Object object) {
if (!someList.isEmpty() && someList.get(0) instanceof Entry) {
entries = (List<Entry>)object;
}
if (!someList.isEmpty() && someList.get(0) instanceof Profile) {
List<Profile> list = (List<Profile>)object;
list.sort(Profile::compare);
currentProfile = list.get(0);
}

} else if (object instanceof ResponseBody) {
treatments = (ResponseBody)object;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.eveningoutpost.dexdrip.cgm.nsfollow.messages;

import com.google.gson.annotations.Expose;

import java.util.Locale;

public class BasalProfileEntry {
@Expose
public String time; // 01:00 etc.
@Expose
public int timeAsSeconds;
@Expose
public double value;

public BasalProfileEntry(int timeAsSeconds, double value) {
this.timeAsSeconds = timeAsSeconds;
this.value = value;
this.time = String.format((Locale) null, "%02d:00", timeAsSeconds / 3600);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.eveningoutpost.dexdrip.cgm.nsfollow.messages;

import com.google.gson.annotations.Expose;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

public class Profile extends BaseMessage {
@Expose
public String _id;
@Expose
public String defaultProfile;
@Expose
public String startDate; // ISO date time
@Expose
public String created_at; // ISO date time
@Expose
public HashMap<String, SingleProfile> store;

public static int compare(Profile a, Profile b) {
return b.startDate.compareTo(a.startDate);
}

public List<Double> getDefaultBasalProfile() {
SingleProfile singleProfile = store.get(defaultProfile);

if (singleProfile == null) {
return new ArrayList<>();
}

ArrayList<BasalProfileEntry> profileFromNS = singleProfile.basal;

int oneHourAsSeconds = 3600;

for (int i = 0; profileFromNS.size() >= i + 1; i++) {
int nextIndex = i + 1;

if (profileFromNS.size() <= nextIndex) {
break;
}

BasalProfileEntry profileEntry = profileFromNS.get(i);
BasalProfileEntry nextProfileEntry = profileFromNS.get(i + 1);

int nextTimeAsSeconds = profileEntry.timeAsSeconds + oneHourAsSeconds;

if (nextProfileEntry.timeAsSeconds > nextTimeAsSeconds) {
profileFromNS.add(nextIndex, new BasalProfileEntry(nextTimeAsSeconds, profileEntry.value));
}
}

return profileFromNS.stream().map(x -> x.value)
.collect(Collectors.toList());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.eveningoutpost.dexdrip.cgm.nsfollow.messages;

import com.google.gson.annotations.Expose;

import java.util.ArrayList;

public class SingleProfile {
@Expose
public ArrayList<BasalProfileEntry> basal;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;

Expand All @@ -35,6 +37,56 @@ public static List<Float> load(final String ref) {
return JsonStringToFloatList(Pref.getString(getPrefix(ref), ""));
}


static int getHourOfTheDay(long timestamp) {
Calendar calendar = Calendar.getInstance();

calendar.setTimeInMillis(timestamp);

return calendar.get(Calendar.HOUR_OF_DAY);
}

static int getDifferenceInFullDays(long timestampA, long timestampB) {
Calendar calendarA = Calendar.getInstance();
Calendar calendarB = Calendar.getInstance();

calendarA.setTimeInMillis(timestampA);
calendarB.setTimeInMillis(timestampB);

return calendarB.get(Calendar.DAY_OF_YEAR) - calendarA.get(Calendar.DAY_OF_YEAR) + 1;
}

public static List<BasalProfileEntryTimed> loadForTimeSpan(final String ref, long startTime, long endTime) {
final List<Float> profile = load(ref);

int fullDays = getDifferenceInFullDays(startTime, endTime);

List<Float> profileForAllDays = new ArrayList<>();

for (int i = 0; i < fullDays; i++) {
profileForAllDays.addAll(profile);
}

int startHour = getHourOfTheDay(startTime);
int endHour = getHourOfTheDay(endTime);

List<Float> sublist = profileForAllDays.subList(startHour, profileForAllDays.size() - 24 + endHour);
List<BasalProfileEntryTimed> timed = new ArrayList<>();

Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(startTime);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);

sublist.forEach(entry -> {
timed.add(new BasalProfileEntryTimed(entry, calendar.getTimeInMillis()));
calendar.add(Calendar.HOUR_OF_DAY, 1);
});

return timed;
}

public static String getActiveRateName() {
return Pref.getString(ACTIVE_BASAL_PROFILE, "1");
}
Expand Down Expand Up @@ -113,4 +165,4 @@ public static String getAllProfilesAsJson() {
return profiles.length() > 0 ? profiles.toString() : null;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.eveningoutpost.dexdrip.profileeditor;

public class BasalProfileEntryTimed {
public Float absolute = null;
public Long timestamp = null;

BasalProfileEntryTimed(Float absolute, Long timestamp) {
this.absolute = absolute;
this.timestamp = timestamp;
}
}
Loading