Merge branch 'dev2_dana_combo_only' of github.com:nightscout/AndroidAPS into dev2_dana_combo_only

This commit is contained in:
Andy Rozman 2021-05-05 16:59:01 +01:00
commit 3b35c01f22
112 changed files with 2385 additions and 4894 deletions

View file

@ -111,7 +111,7 @@ android {
defaultConfig {
multiDexEnabled true
versionCode 1500
version "2.8.2.1-dev-e3"
version "2.8.2.1-dev-e4"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'

View file

@ -1,7 +1,5 @@
package info.nightscout.androidaps.data.defaultProfile
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.ProfileImplOld
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.GlucoseUnit

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.data.defaultProfile
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.ProfileImplOld
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.GlucoseUnit

View file

@ -4,8 +4,6 @@ import android.content.Context;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import androidx.annotation.Nullable;
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
import com.j256.ormlite.dao.CloseableIterator;
import com.j256.ormlite.dao.Dao;
@ -20,9 +18,6 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import javax.inject.Inject;
@ -32,7 +27,6 @@ import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.utils.DateUtil;
@ -51,12 +45,9 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
@Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject OpenHumansUploader openHumansUploader;
@Inject ActivePlugin activePlugin;
@Inject NSUpload nsUpload;
@Inject DateUtil dateUtil;
public static final String DATABASE_NAME = "AndroidAPSDb";
public static final String DATABASE_DANARHISTORY = "DanaRHistory";
public static final String DATABASE_DBREQUESTS = "DBRequests";
private static final int DATABASE_VERSION = 13;
@ -76,10 +67,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
try {
aapsLogger.info(LTag.DATABASE, "onCreate");
TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class);
TableUtils.createTableIfNotExists(connectionSource, DbRequest.class);
TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class);
TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class);
TableUtils.createTableIfNotExists(connectionSource, InsightHistoryOffset.class);
TableUtils.createTableIfNotExists(connectionSource, InsightBolusID.class);
TableUtils.createTableIfNotExists(connectionSource, InsightPumpID.class);
@ -103,10 +90,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
if (oldVersion < 7) {
aapsLogger.info(LTag.DATABASE, "onUpgrade");
TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true);
TableUtils.dropTable(connectionSource, DbRequest.class, true);
TableUtils.dropTable(connectionSource, TemporaryBasal.class, true);
TableUtils.dropTable(connectionSource, ExtendedBolus.class, true);
onCreate(database, connectionSource);
} else if (oldVersion < 10) {
TableUtils.createTableIfNotExists(connectionSource, InsightHistoryOffset.class);
@ -149,15 +132,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
public void resetDatabases() {
try {
TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true);
TableUtils.dropTable(connectionSource, DbRequest.class, true);
TableUtils.dropTable(connectionSource, TemporaryBasal.class, true);
TableUtils.dropTable(connectionSource, ExtendedBolus.class, true);
TableUtils.dropTable(connectionSource, OmnipodHistoryRecord.class, true);
TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class);
TableUtils.createTableIfNotExists(connectionSource, DbRequest.class);
TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class);
TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class);
TableUtils.createTableIfNotExists(connectionSource, OmnipodHistoryRecord.class);
updateEarliestDataChange(0);
} catch (SQLException e) {
@ -177,22 +152,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
// ------------------ getDao -------------------------------------------
private Dao<DanaRHistoryRecord, String> getDaoDanaRHistory() throws SQLException {
return getDao(DanaRHistoryRecord.class);
}
private Dao<DbRequest, String> getDaoDbRequest() throws SQLException {
return getDao(DbRequest.class);
}
private Dao<TemporaryBasal, Long> getDaoTemporaryBasal() throws SQLException {
return getDao(TemporaryBasal.class);
}
private Dao<ExtendedBolus, Long> getDaoExtendedBolus() throws SQLException {
return getDao(ExtendedBolus.class);
}
private Dao<InsightPumpID, Long> getDaoInsightPumpID() throws SQLException {
return getDao(InsightPumpID.class);
}
@ -213,76 +172,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return getDao(OHQueueItem.class);
}
public long roundDateToSec(long date) {
long rounded = date - date % 1000;
if (rounded != date)
aapsLogger.debug(LTag.DATABASE, "Rounding " + date + " to " + rounded);
return rounded;
}
// ------------- DbRequests handling -------------------
public void create(DbRequest dbr) throws SQLException {
getDaoDbRequest().create(dbr);
}
public int delete(DbRequest dbr) {
try {
return getDaoDbRequest().delete(dbr);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return 0;
}
public int deleteDbRequest(String nsClientId) {
try {
return getDaoDbRequest().deleteById(nsClientId);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return 0;
}
public void deleteDbRequestbyMongoId(String action, String id) {
try {
QueryBuilder<DbRequest, String> queryBuilder = getDaoDbRequest().queryBuilder();
// By nsID
Where where = queryBuilder.where();
where.eq("_id", id).and().eq("action", action);
queryBuilder.limit(10L);
PreparedQuery<DbRequest> preparedQuery = queryBuilder.prepare();
List<DbRequest> dbList = getDaoDbRequest().query(preparedQuery);
for (DbRequest r : dbList) delete(r);
// By nsClientID
where = queryBuilder.where();
where.eq("nsClientID", id).and().eq("action", action);
queryBuilder.limit(10L);
preparedQuery = queryBuilder.prepare();
dbList = getDaoDbRequest().query(preparedQuery);
for (DbRequest r : dbList) delete(r);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void deleteAllDbRequests() {
try {
TableUtils.clearTable(connectionSource, DbRequest.class);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public CloseableIterator getDbRequestIterator() {
try {
return getDaoDbRequest().closeableIterator();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
return null;
}
}
public static void updateEarliestDataChange(long newDate) {
if (earliestDataChange == null) {
earliestDataChange = newDate;
@ -293,433 +182,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
}
}
// ----------------- DanaRHistory handling --------------------
public void createOrUpdate(DanaRHistoryRecord record) {
try {
getDaoDanaRHistory().createOrUpdate(record);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public List<DanaRHistoryRecord> getDanaRHistoryRecordsByType(byte type) {
List<DanaRHistoryRecord> historyList;
try {
QueryBuilder<DanaRHistoryRecord, String> queryBuilder = getDaoDanaRHistory().queryBuilder();
queryBuilder.orderBy("recordDate", false);
Where where = queryBuilder.where();
where.eq("recordCode", type);
queryBuilder.limit(200L);
PreparedQuery<DanaRHistoryRecord> preparedQuery = queryBuilder.prepare();
historyList = getDaoDanaRHistory().query(preparedQuery);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
historyList = new ArrayList<>();
}
return historyList;
}
// ------------ TemporaryBasal handling ---------------
//return true if new record was created
public boolean createOrUpdate(TemporaryBasal tempBasal) {
try {
TemporaryBasal old;
tempBasal.date = roundDateToSec(tempBasal.date);
if (tempBasal.source == Source.PUMP) {
// check for changed from pump change in NS
QueryBuilder<TemporaryBasal, Long> queryBuilder = getDaoTemporaryBasal().queryBuilder();
Where where = queryBuilder.where();
where.eq("pumpId", tempBasal.pumpId);
PreparedQuery<TemporaryBasal> preparedQuery = queryBuilder.prepare();
List<TemporaryBasal> trList = getDaoTemporaryBasal().query(preparedQuery);
if (trList.size() > 0) {
// do nothing, pump history record cannot be changed
aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
return false;
}
// search by date (in case its standard record that has become pump record)
QueryBuilder<TemporaryBasal, Long> queryBuilder2 = getDaoTemporaryBasal().queryBuilder();
Where where2 = queryBuilder2.where();
where2.eq("date", tempBasal.date);
PreparedQuery<TemporaryBasal> preparedQuery2 = queryBuilder2.prepare();
List<TemporaryBasal> trList2 = getDaoTemporaryBasal().query(preparedQuery2);
if (trList2.size() > 0) {
old = trList2.get(0);
old.copyFromPump(tempBasal);
old.source = Source.PUMP;
aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Updated record with Pump Data : " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
getDaoTemporaryBasal().update(old);
openHumansUploader.enqueueTemporaryBasal(old);
updateEarliestDataChange(tempBasal.date);
// scheduleTemporaryBasalChange();
return false;
}
getDaoTemporaryBasal().create(tempBasal);
openHumansUploader.enqueueTemporaryBasal(tempBasal);
aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
updateEarliestDataChange(tempBasal.date);
// scheduleTemporaryBasalChange();
return true;
}
if (tempBasal.source == Source.NIGHTSCOUT) {
old = getDaoTemporaryBasal().queryForId(tempBasal.date);
if (old != null) {
if (!old.isAbsolute && tempBasal.isAbsolute) { // converted to absolute by "ns_sync_use_absolute"
// so far ignore, do not convert back because it may not be accurate
return false;
}
if (!old.isEqual(tempBasal)) {
long oldDate = old.date;
getDaoTemporaryBasal().delete(old); // need to delete/create because date may change too
old.copyFrom(tempBasal);
getDaoTemporaryBasal().create(old);
openHumansUploader.enqueueTemporaryBasal(old);
aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Updating record by date from: " + Source.getString(tempBasal.source) + " " + old.toString());
updateEarliestDataChange(oldDate);
updateEarliestDataChange(old.date);
// scheduleTemporaryBasalChange();
return true;
}
return false;
}
// find by NS _id
if (tempBasal._id != null) {
QueryBuilder<TemporaryBasal, Long> queryBuilder = getDaoTemporaryBasal().queryBuilder();
Where where = queryBuilder.where();
where.eq("_id", tempBasal._id);
PreparedQuery<TemporaryBasal> preparedQuery = queryBuilder.prepare();
List<TemporaryBasal> trList = getDaoTemporaryBasal().query(preparedQuery);
if (trList.size() > 0) {
old = trList.get(0);
if (!old.isEqual(tempBasal)) {
long oldDate = old.date;
getDaoTemporaryBasal().delete(old); // need to delete/create because date may change too
old.copyFrom(tempBasal);
getDaoTemporaryBasal().create(old);
openHumansUploader.enqueueTemporaryBasal(old);
aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Updating record by _id from: " + Source.getString(tempBasal.source) + " " + old.toString());
updateEarliestDataChange(oldDate);
updateEarliestDataChange(old.date);
// scheduleTemporaryBasalChange();
return true;
}
}
}
getDaoTemporaryBasal().create(tempBasal);
openHumansUploader.enqueueTemporaryBasal(tempBasal);
aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
updateEarliestDataChange(tempBasal.date);
// scheduleTemporaryBasalChange();
return true;
}
if (tempBasal.source == Source.USER) {
getDaoTemporaryBasal().create(tempBasal);
openHumansUploader.enqueueTemporaryBasal(tempBasal);
aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
updateEarliestDataChange(tempBasal.date);
// scheduleTemporaryBasalChange();
return true;
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return false;
}
public void delete(TemporaryBasal tempBasal) {
try {
getDaoTemporaryBasal().delete(tempBasal);
openHumansUploader.enqueueTemporaryBasal(tempBasal, true);
updateEarliestDataChange(tempBasal.date);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
// scheduleTemporaryBasalChange();
}
public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long mills, boolean ascending) {
try {
List<TemporaryBasal> tempbasals;
QueryBuilder<TemporaryBasal, Long> queryBuilder = getDaoTemporaryBasal().queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.ge("date", mills);
PreparedQuery<TemporaryBasal> preparedQuery = queryBuilder.prepare();
tempbasals = getDaoTemporaryBasal().query(preparedQuery);
return tempbasals;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<TemporaryBasal>();
}
/*
{
"_id": "59232e1ddd032d04218dab00",
"eventType": "Temp Basal",
"duration": 60,
"percent": -50,
"created_at": "2017-05-22T18:29:57Z",
"enteredBy": "AndroidAPS",
"notes": "Basal Temp Start 50% 60.0 min",
"NSCLIENT_ID": 1495477797863,
"mills": 1495477797000,
"mgdl": 194.5,
"endmills": 1495481397000
}
*/
public TemporaryBasal findTempBasalByPumpId(Long pumpId) {
try {
QueryBuilder<TemporaryBasal, Long> queryBuilder = null;
queryBuilder = getDaoTemporaryBasal().queryBuilder();
queryBuilder.orderBy("date", false);
Where where = queryBuilder.where();
where.eq("pumpId", pumpId);
PreparedQuery<TemporaryBasal> preparedQuery = queryBuilder.prepare();
List<TemporaryBasal> list = getDaoTemporaryBasal().query(preparedQuery);
if (list.size() > 0)
return list.get(0);
else
return null;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return null;
}
// ------------ ExtendedBolus handling ---------------
public ExtendedBolus getExtendedBolusByPumpId(long pumpId) {
try {
return getDaoExtendedBolus().queryBuilder()
.where().eq("pumpId", pumpId)
.queryForFirst();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return null;
}
public void delete(ExtendedBolus extendedBolus) {
try {
getDaoExtendedBolus().delete(extendedBolus);
openHumansUploader.enqueueExtendedBolus(extendedBolus, true);
updateEarliestDataChange(extendedBolus.date);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
// scheduleExtendedBolusChange();
}
/*
{
"_id": "5924898d577eb0880e355337",
"eventType": "Combo Bolus",
"duration": 120,
"splitNow": 0,
"splitExt": 100,
"enteredinsulin": 1,
"relative": 1,
"created_at": "2017-05-23T19:12:14Z",
"enteredBy": "AndroidAPS",
"NSCLIENT_ID": 1495566734628,
"mills": 1495566734000,
"mgdl": 106
}
*/
// ---------------- ProfileSwitch handling ---------------
/*
public boolean createOrUpdate(ProfileSwitch profileSwitch) {
try {
ProfileSwitch old;
profileSwitch.date = roundDateToSec(profileSwitch.date);
if (profileSwitch.source == Source.NIGHTSCOUT) {
old = getDaoProfileSwitch().queryForId(profileSwitch.date);
if (old != null) {
if (!old.isEqual(profileSwitch)) {
profileSwitch.source = old.source;
profileSwitch.profileName = old.profileName; // preserver profileName to prevent multiple CPP extension
getDaoProfileSwitch().delete(old); // need to delete/create because date may change too
getDaoProfileSwitch().create(profileSwitch);
aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: Updating record by date from: " + Source.getString(profileSwitch.source) + " " + old.toString());
openHumansUploader.enqueueProfileSwitch(profileSwitch);
scheduleProfileSwitchChange();
return true;
}
return false;
}
// find by NS _id
if (profileSwitch._id != null) {
QueryBuilder<ProfileSwitch, Long> queryBuilder = getDaoProfileSwitch().queryBuilder();
Where where = queryBuilder.where();
where.eq("_id", profileSwitch._id);
PreparedQuery<ProfileSwitch> preparedQuery = queryBuilder.prepare();
List<ProfileSwitch> trList = getDaoProfileSwitch().query(preparedQuery);
if (trList.size() > 0) {
old = trList.get(0);
if (!old.isEqual(profileSwitch)) {
getDaoProfileSwitch().delete(old); // need to delete/create because date may change too
old.copyFrom(profileSwitch);
getDaoProfileSwitch().create(old);
aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: Updating record by _id from: " + Source.getString(profileSwitch.source) + " " + old.toString());
openHumansUploader.enqueueProfileSwitch(old);
scheduleProfileSwitchChange();
return true;
}
}
}
// look for already added percentage from NS
profileSwitch.profileName = PercentageSplitter.INSTANCE.pureName(profileSwitch.profileName);
getDaoProfileSwitch().create(profileSwitch);
aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString());
openHumansUploader.enqueueProfileSwitch(profileSwitch);
scheduleProfileSwitchChange();
return true;
}
if (profileSwitch.source == Source.USER) {
getDaoProfileSwitch().create(profileSwitch);
aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString());
openHumansUploader.enqueueProfileSwitch(profileSwitch);
scheduleProfileSwitchChange();
return true;
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return false;
}
public void delete(ProfileSwitch profileSwitch) {
try {
getDaoProfileSwitch().delete(profileSwitch);
openHumansUploader.enqueueProfileSwitch(profileSwitch, true);
scheduleProfileSwitchChange();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
private void scheduleProfileSwitchChange() {
class PostRunnable implements Runnable {
public void run() {
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileNeedsUpdate");
rxBus.send(new EventReloadProfileSwitchData());
rxBus.send(new EventProfileNeedsUpdate());
scheduledProfileSwitchEventPost = null;
}
}
// prepare task for execution in 1 sec
// cancel waiting task to prevent sending multiple posts
if (scheduledProfileSwitchEventPost != null)
scheduledProfileSwitchEventPost.cancel(false);
Runnable task = new PostRunnable();
final int sec = 1;
scheduledProfileSwitchEventPost = profileSwitchEventWorker.schedule(task, sec, TimeUnit.SECONDS);
}
*/
/*
{
"_id":"592fa43ed97496a80da913d2",
"created_at":"2017-06-01T05:20:06Z",
"eventType":"Profile Switch",
"profile":"2016 +30%",
"units":"mmol",
"enteredBy":"sony",
"NSCLIENT_ID":1496294454309,
}
*/
/*
public void createProfileSwitchFromJsonIfNotExists(JSONObject trJson) {
try {
ProfileSwitch profileSwitch = new ProfileSwitch(StaticInjector.Companion.getInstance());
profileSwitch.date = trJson.getLong("mills");
if (trJson.has("duration"))
profileSwitch.durationInMinutes = trJson.getInt("duration");
profileSwitch._id = trJson.getString("_id");
profileSwitch.profileName = trJson.getString("profile");
profileSwitch.isCPP = trJson.has("CircadianPercentageProfile");
profileSwitch.source = Source.NIGHTSCOUT;
if (trJson.has("timeshift"))
profileSwitch.timeshift = trJson.getInt("timeshift");
if (trJson.has("percentage"))
profileSwitch.percentage = trJson.getInt("percentage");
if (trJson.has("profileJson"))
profileSwitch.profileJson = trJson.getString("profileJson");
else {
ProfileSource profileSource = activePlugin.getActiveProfileSource();
ProfileStore store = profileSource.getProfile();
if (store != null) {
PureProfile profile = store.getSpecificProfile(profileSwitch.profileName);
if (profile != null) {
profileSwitch.profileJson = profile.getJsonObject().toString();
aapsLogger.debug(LTag.DATABASE, "Profile switch prefilled with JSON from local store");
// Update data in NS
nsUpload.updateProfileSwitch(profileSwitch, dateUtil);
} else {
aapsLogger.debug(LTag.DATABASE, "JSON for profile switch doesn't exist. Ignoring: " + trJson.toString());
return;
}
} else {
aapsLogger.debug(LTag.DATABASE, "Store for profile switch doesn't exist. Ignoring: " + trJson.toString());
return;
}
}
if (trJson.has("profilePlugin"))
profileSwitch.profilePlugin = trJson.getString("profilePlugin");
createOrUpdate(profileSwitch);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception: " + trJson.toString(), e);
}
}
public void deleteProfileSwitchById(String _id) {
ProfileSwitch stored = findProfileSwitchById(_id);
if (stored != null) {
aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: Removing ProfileSwitch record from database: " + stored.toString());
delete(stored);
scheduleProfileSwitchChange();
}
}
public ProfileSwitch findProfileSwitchById(String _id) {
try {
QueryBuilder<ProfileSwitch, Long> queryBuilder = getDaoProfileSwitch().queryBuilder();
Where where = queryBuilder.where();
where.eq("_id", _id);
PreparedQuery<ProfileSwitch> preparedQuery = queryBuilder.prepare();
List<ProfileSwitch> list = getDaoProfileSwitch().query(preparedQuery);
if (list.size() == 1) {
return list.get(0);
} else {
return null;
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return null;
}
*/
// ---------------- Insight history handling ---------------
public void createOrUpdate(InsightHistoryOffset offset) {
@ -924,14 +386,4 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
}
return 0L;
}
public long getCountOfAllRows() {
try {
return getDaoExtendedBolus().countOf()
+ getDaoTemporaryBasal().countOf();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return 0L;
}
}

View file

@ -5,8 +5,6 @@ import androidx.annotation.Nullable;
import com.j256.ormlite.dao.CloseableIterator;
import org.json.JSONObject;
import java.sql.SQLException;
import java.util.List;
@ -23,61 +21,24 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface {
@Inject DatabaseHelperProvider() {
}
@Override public void createOrUpdate(@NonNull DanaRHistoryRecord record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@Override public void createOrUpdate(@NonNull OmnipodHistoryRecord record) {
MainApp.Companion.getDbHelper().createOrUpdate(record);
}
@NonNull @Override public List<DanaRHistoryRecord> getDanaRHistoryRecordsByType(byte type) {
return MainApp.Companion.getDbHelper().getDanaRHistoryRecordsByType(type);
}
@Override public long size(@NonNull String table) {
return MainApp.Companion.getDbHelper().size(table);
}
@Override public void create(@NonNull DbRequest record) {
try {
MainApp.Companion.getDbHelper().create(record);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override public void deleteAllDbRequests() {
MainApp.Companion.getDbHelper().deleteAllDbRequests();
}
@Override public int deleteDbRequest(@NonNull String id) {
return MainApp.Companion.getDbHelper().deleteDbRequest(id);
}
@Override public void deleteDbRequestbyMongoId(@NonNull String action, @NonNull String _id) {
MainApp.Companion.getDbHelper().deleteDbRequestbyMongoId(action, _id);
}
@NonNull @Override public CloseableIterator<DbRequest> getDbRequestIterator() {
return MainApp.Companion.getDbHelper().getDbRequestIterator();
}
@Override public long roundDateToSec(long date) {
return MainApp.Companion.getDbHelper().roundDateToSec(date);
}
@Override public boolean createOrUpdate(@NonNull TemporaryBasal tempBasal) {
return MainApp.Companion.getDbHelper().createOrUpdate(tempBasal);
// return MainApp.Companion.getDbHelper().createOrUpdate(tempBasal);
return false;
}
@Nullable @Override public TemporaryBasal findTempBasalByPumpId(long id) {
return MainApp.Companion.getDbHelper().findTempBasalByPumpId(id);
// return MainApp.Companion.getDbHelper().findTempBasalByPumpId(id);
return null;
}
@Deprecated
@NonNull @Override public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long mills, boolean ascending) {
return MainApp.Companion.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending);
// return MainApp.Companion.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending);
return null;
}
@NonNull @Override public List<OmnipodHistoryRecord> getAllOmnipodHistoryRecordsFromTimestamp(long timestamp, boolean ascending) {
@ -101,11 +62,12 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface {
}
@Override public void delete(@NonNull ExtendedBolus extendedBolus) {
MainApp.Companion.getDbHelper().delete(extendedBolus);
// MainApp.Companion.getDbHelper().delete(extendedBolus);
}
@Nullable @Override public ExtendedBolus getExtendedBolusByPumpId(long pumpId) {
return MainApp.Companion.getDbHelper().getExtendedBolusByPumpId(pumpId);
// return MainApp.Companion.getDbHelper().getExtendedBolusByPumpId(pumpId);
return null;
}
@Nullable @Override public InsightBolusID getInsightBolusID(@NonNull String pumpSerial, int bolusID, long timestamp) {
@ -140,10 +102,6 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface {
MainApp.Companion.getDbHelper().clearOpenHumansQueue();
}
@Override public long getCountOfAllRows() {
return MainApp.Companion.getDbHelper().getCountOfAllRows();
}
@Override public void removeAllOHQueueItemsWithIdSmallerThan(long id) {
MainApp.Companion.getDbHelper().removeAllOHQueueItemsWithIdSmallerThan(id);
}

View file

@ -7,6 +7,7 @@ import dagger.android.AndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.automation.di.AutomationModule
import info.nightscout.androidaps.combo.di.ComboModule
import info.nightscout.androidaps.dana.di.DanaHistoryModule
import info.nightscout.androidaps.di.CoreModule
import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule
@ -45,6 +46,7 @@ import javax.inject.Singleton
UIModule::class,
CoreModule::class,
DanaModule::class,
DanaHistoryModule::class,
DanaRModule::class,
DanaRSModule::class,
ComboModule::class,

View file

@ -6,32 +6,34 @@ import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.db.DatabaseHelperProvider
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl
import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.PumpSyncImplementation
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.androidNotification.NotificationHolderImpl
import info.nightscout.androidaps.utils.buildHelper.ConfigImpl
import info.nightscout.androidaps.utils.resources.IconsProviderImplementation
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.rx.DefaultAapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.storage.FileStorage
import info.nightscout.androidaps.utils.storage.Storage
import javax.inject.Singleton
@Suppress("unused")
@Module(includes = [
AppModule.AppBindings::class
@ -64,13 +66,9 @@ open class AppModule {
@Provides
@Singleton
fun providesUploadQueue(
aapsLogger: AAPSLogger,
databaseHelper: DatabaseHelperInterface,
context: Context,
sp: SP,
rxBus: RxBusWrapper
): UploadQueueAdminInterface = UploadQueue(aapsLogger, databaseHelper, context, sp, rxBus)
fun provideProfileFunction(aapsLogger: AAPSLogger, sp: SP, resourceHelper: ResourceHelper, activePlugin: ActivePlugin, repository: AppRepository, dateUtil: DateUtil): ProfileFunction {
return ProfileFunctionImplementation(aapsLogger, sp, resourceHelper, activePlugin, repository, dateUtil)
}
@Module
interface AppBindings {
@ -88,7 +86,6 @@ open class AppModule {
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): LoopInterface
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
@Binds fun bindUploadQueueAdminInterfaceToUploadQueue(uploadQueueAdminInterface: UploadQueueAdminInterface) : UploadQueueInterface
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
@Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync

View file

@ -29,7 +29,6 @@ import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment
import info.nightscout.androidaps.plugins.general.wear.WearFragment
import info.nightscout.androidaps.plugins.insulin.InsulinFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment
@ -46,8 +45,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesAutomationFragment(): AutomationFragment
@ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment
@ContributesAndroidInjector
abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
@ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
@ContributesAndroidInjector abstract fun contributesFoodFragment(): FoodFragment
@ContributesAndroidInjector abstract fun contributesInsulinFragment(): InsulinFragment
@ -58,10 +56,8 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesOverviewFragment(): OverviewFragment
@ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment
@ContributesAndroidInjector abstract fun contributesMaintenanceFragment(): MaintenanceFragment
@ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment
@ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment
@ContributesAndroidInjector
abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment
@ContributesAndroidInjector abstract fun contributesWearFragment(): WearFragment
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
@ -85,8 +81,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesEditEventDialog(): EditEventDialog
@ContributesAndroidInjector abstract fun contributesEditTriggerDialog(): EditTriggerDialog
@ContributesAndroidInjector
abstract fun contributesEditQuickWizardDialog(): EditQuickWizardDialog
@ContributesAndroidInjector abstract fun contributesEditQuickWizardDialog(): EditQuickWizardDialog
@ContributesAndroidInjector abstract fun contributesExtendedBolusDialog(): ExtendedBolusDialog
@ContributesAndroidInjector abstract fun contributesFillDialog(): FillDialog
@ -102,8 +97,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog
@ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog
@ContributesAndroidInjector
abstract fun contributesExchangeAuthTokenDialot(): OpenHumansLoginActivity.ExchangeAuthTokenDialog
@ContributesAndroidInjector abstract fun contributesExchangeAuthTokenDialog(): OpenHumansLoginActivity.ExchangeAuthTokenDialog
@ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck
}

View file

@ -37,7 +37,6 @@ import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin
import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
@ -192,12 +191,6 @@ abstract class PluginsModule {
@IntKey(220)
abstract fun bindOpenAPSSMBPlugin(plugin: OpenAPSSMBPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(230)
abstract fun bindNSProfilePlugin(plugin: NSProfilePlugin): PluginBase
@Binds
@NotNSClient
@IntoMap

View file

@ -9,7 +9,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.source.*
@Module
@ -24,7 +24,7 @@ abstract class WorkersModule {
@ContributesAndroidInjector abstract fun contributesTomatoWorker(): TomatoPlugin.TomatoWorker
@ContributesAndroidInjector abstract fun contributesEversenseWorker(): EversensePlugin.EversenseWorker
@ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): LocalProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker
@ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientAddUpdateWorker
@ContributesAndroidInjector abstract fun contributesNSClientAddAckWorker(): NSClientAddAckWorker

View file

@ -403,7 +403,7 @@ open class LoopPlugin @Inject constructor(
val waiting = PumpEnactResult(injector)
waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting
if (resultAfterConstraints.bolusRequested) lastRun.smbSetByPump = waiting
if (resultAfterConstraints.bolusRequested()) lastRun.smbSetByPump = waiting
rxBus.send(EventLoopUpdateGui())
fabricPrivacy.logCustom("APSRequest")
applyTBRRequest(resultAfterConstraints, profile, object : Callback() {
@ -600,7 +600,7 @@ open class LoopPlugin @Inject constructor(
}
private fun applySMBRequest(request: APSResult, callback: Callback?) {
if (!request.bolusRequested) {
if (!request.bolusRequested()) {
return
}
val pump = activePlugin.activePump

View file

@ -40,7 +40,6 @@ class DetermineBasalResultAMA private constructor(injector: HasAndroidInjector)
tempBasalRequested = false
}
}
bolusRequested = false
}
override fun newAndClone(injector: HasAndroidInjector): DetermineBasalResultAMA {

View file

@ -35,7 +35,6 @@ class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector)
duration = -1
}
if (result.has("units")) {
bolusRequested = true
smb = result.getDouble("units")
} else {
smb = 0.0

View file

@ -28,7 +28,7 @@ class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(i
}
override fun specialActionEnabled(): Boolean =
NSClientService.isConnected && NSClientService.hasWriteAuth
nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true
override fun specialAction(activity: FragmentActivity, input: String) {
objectivesPlugin.completeObjectives(activity, input)

View file

@ -7,13 +7,13 @@ import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.dana.database.DanaHistoryDatabase
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.interfaces.PumpSync
import info.nightscout.androidaps.logging.AAPSLogger
@ -38,7 +38,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var importExportPrefs: ImportExportPrefs
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var repository: AppRepository
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var danaHistoryDatabase: DanaHistoryDatabase
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var pumpSync: PumpSync
@ -68,8 +68,8 @@ class MaintenanceFragment : DaggerFragment() {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.reset_db_confirm), Runnable {
compositeDisposable.add(
fromAction {
databaseHelper.resetDatabases()
repository.clearDatabases()
danaHistoryDatabase.clearAllTables()
dataSyncSelector.resetToNextFullSync()
pumpSync.connectNewPump()
}

View file

@ -2,14 +2,14 @@ package info.nightscout.androidaps.plugins.general.nsclient
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.extensions.toJson
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.extensions.toJson
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
@ -21,9 +21,27 @@ class DataSyncSelectorImplementation @Inject constructor(
private val profileFunction: ProfileFunction,
private val nsClientPlugin: NSClientPlugin,
private val activePlugin: ActivePlugin,
private val appRepository: AppRepository
private val appRepository: AppRepository,
private val localProfilePlugin: LocalProfilePlugin
) : DataSyncSelector {
override fun doUpload() {
if (sp.getBoolean(R.string.key_ns_upload, true)) {
processChangedBolusesCompat()
processChangedCarbsCompat()
processChangedBolusCalculatorResultsCompat()
processChangedTemporaryBasalsCompat()
processChangedExtendedBolusesCompat()
processChangedProfileSwitchesCompat()
processChangedGlucoseValuesCompat()
processChangedTempTargetsCompat()
processChangedFoodsCompat()
processChangedTherapyEventsCompat()
processChangedDeviceStatusesCompat()
processChangedProfileStore()
}
}
override fun resetToNextFullSync() {
sp.remove(R.string.key_ns_temporary_target_last_synced_id)
sp.remove(R.string.key_ns_glucose_value_last_synced_id)
@ -36,6 +54,7 @@ class DataSyncSelectorImplementation @Inject constructor(
sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
sp.remove(R.string.key_ns_therapy_event_last_synced_id)
sp.remove(R.string.key_ns_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
}
override fun confirmLastBolusIdIfGreater(lastSynced: Long) {
@ -445,4 +464,18 @@ class DataSyncSelectorImplementation @Inject constructor(
}
return false
}
override fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
}
override fun processChangedProfileStore() {
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(R.string.key_local_profile_last_change, 0)
if (lastChange == 0L) return
localProfilePlugin.createProfileStore()
val profileJson = localProfilePlugin.profile?.data ?: return
if (lastChange > lastSync)
nsClientPlugin.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()))
}
}

View file

@ -147,7 +147,7 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs" + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedCarbsCompat()
}
@ -166,7 +166,7 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult" + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResultsCompat()
}
@ -185,7 +185,7 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal" + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
}
@ -204,7 +204,7 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus" + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
}
@ -223,7 +223,7 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch" + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
}
@ -242,10 +242,15 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastDeviceStatusIdIfGreater(deviceStatus.id)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus" + deviceStatus.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus " + deviceStatus.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedDeviceStatusesCompat()
}
is PairProfileStore -> {
dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync)
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id))
}
}
return ret
}

View file

@ -14,6 +14,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.*
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -43,13 +44,11 @@ class NSClientAddUpdateWorker(
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var config: Config
@Inject lateinit var repository: AppRepository
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uel: UserEntryLogger
override fun doWork(): Result {
val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT
if (!acceptNSData) return Result.success()
val treatments = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
@ -73,6 +72,7 @@ class NSClientAddUpdateWorker(
if (mills > latestDateInReceivedData) latestDateInReceivedData = mills
if (insulin > 0) {
if (sp.getBoolean(R.string.key_ns_receive_insulin, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
bolusFromJson(json)?.let { bolus ->
repository.runTransactionForResult(SyncNsBolusTransaction(bolus, invalidateByNsOnly = false))
.doOnError {
@ -101,7 +101,9 @@ class NSClientAddUpdateWorker(
}
} ?: aapsLogger.error("Error parsing bolus json $json")
}
}
if (carbs > 0) {
if (sp.getBoolean(R.string.key_ns_receive_carbs, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
carbsFromJson(json)?.let { carb ->
repository.runTransactionForResult(SyncNsCarbsTransaction(carb, invalidateByNsOnly = false))
.doOnError {
@ -130,6 +132,7 @@ class NSClientAddUpdateWorker(
}
} ?: aapsLogger.error("Error parsing bolus json $json")
}
}
// Convert back emulated TBR -> EB
if (eventType == TherapyEvent.Type.TEMPORARY_BASAL.text && json.has("extendedEmulated")) {
val ebJson = json.getJSONObject("extendedEmulated")
@ -140,6 +143,7 @@ class NSClientAddUpdateWorker(
when {
insulin > 0 || carbs > 0 -> Any()
eventType == TherapyEvent.Type.TEMPORARY_TARGET.text ->
if (sp.getBoolean(R.string.key_ns_receive_temp_target, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
temporaryTargetFromJson(json)?.let { temporaryTarget ->
repository.runTransactionForResult(SyncNsTemporaryTargetTransaction(temporaryTarget, invalidateByNsOnly = false))
.doOnError {
@ -180,6 +184,7 @@ class NSClientAddUpdateWorker(
}
}
} ?: aapsLogger.error("Error parsing TT json $json")
}
eventType == TherapyEvent.Type.CANNULA_CHANGE.text ||
eventType == TherapyEvent.Type.INSULIN_CHANGE.text ||
eventType == TherapyEvent.Type.SENSOR_CHANGE.text ||
@ -190,6 +195,7 @@ class NSClientAddUpdateWorker(
eventType == TherapyEvent.Type.EXERCISE.text ||
eventType == TherapyEvent.Type.APS_OFFLINE.text ||
eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text ->
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) {
therapyEventFromJson(json)?.let { therapyEvent ->
repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEvent, invalidateByNsOnly = false))
.doOnError {
@ -224,7 +230,9 @@ class NSClientAddUpdateWorker(
}
}
} ?: aapsLogger.error("Error parsing TherapyEvent json $json")
}
eventType == TherapyEvent.Type.COMBO_BOLUS.text ->
if (config.NSCLIENT) {
extendedBolusFromJson(json)?.let { extendedBolus ->
repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBolus, invalidateByNsOnly = false))
.doOnError {
@ -265,7 +273,9 @@ class NSClientAddUpdateWorker(
}
}
} ?: aapsLogger.error("Error parsing ExtendedBolus json $json")
}
eventType == TherapyEvent.Type.TEMPORARY_BASAL.text ->
if (config.NSCLIENT) {
temporaryBasalFromJson(json)?.let { temporaryBasal ->
repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasal, invalidateByNsOnly = false))
.doOnError {
@ -303,8 +313,10 @@ class NSClientAddUpdateWorker(
}
}
} ?: aapsLogger.error("Error parsing TemporaryBasal json $json")
}
eventType == TherapyEvent.Type.PROFILE_SWITCH.text ->
profileSwitchFromJson(json, dateUtil)?.let { profileSwitch ->
if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
profileSwitchFromJson(json, dateUtil, activePlugin)?.let { profileSwitch ->
repository.runTransactionForResult(SyncNsProfileSwitchTransaction(profileSwitch, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", it)
@ -326,8 +338,10 @@ class NSClientAddUpdateWorker(
aapsLogger.debug(LTag.DATABASE, "Updated nsId ProfileSwitch $it")
}
}
} ?: aapsLogger.error("Error parsing TemporaryBasal json $json")
} ?: aapsLogger.error("Error parsing ProfileSwitch json $json")
}
}
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT)
if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) {
val date = safeGetLong(json, "mills")
val now = System.currentTimeMillis()

View file

@ -12,14 +12,11 @@ import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.NsClientFragmentBinding
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.UploadQueueAdminInterface
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper.fromHtml
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
@ -33,7 +30,6 @@ class NSClientFragment : DaggerFragment() {
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uploadQueue: UploadQueueAdminInterface
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dataSyncSelector: DataSyncSelector
@ -72,18 +68,6 @@ class NSClientFragment : DaggerFragment() {
binding.restart.paintFlags = binding.restart.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.deliverNow.setOnClickListener { nsClientPlugin.resend("GUI") }
binding.deliverNow.paintFlags = binding.deliverNow.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.clearQueue.setOnClickListener {
context?.let { context ->
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.clearqueueconfirm), Runnable {
uel.log(Action.NS_QUEUE_CLEARED, Sources.NSClient)
uploadQueue.clearQueue()
updateGui()
})
}
}
binding.clearQueue.paintFlags = binding.clearQueue.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.showQueue.setOnClickListener { rxBus.send(EventNSClientNewLog("QUEUE", uploadQueue.textList())) }
binding.showQueue.paintFlags = binding.showQueue.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.fullSync.setOnClickListener {
context?.let { context ->
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.full_sync), Runnable {
@ -116,7 +100,6 @@ class NSClientFragment : DaggerFragment() {
binding.log.text = nsClientPlugin.textLog
if (nsClientPlugin.autoscroll) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN)
binding.url.text = nsClientPlugin.url()
binding.queue.text = fromHtml(resourceHelper.gs(R.string.queue) + " <b>" + uploadQueue.size() + "</b>")
binding.status.text = nsClientPlugin.status
}
}

View file

@ -8,13 +8,13 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.transactions.SyncNsTherapyEventTransaction
import info.nightscout.androidaps.extensions.therapyEventFromNsMbg
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.extensions.therapyEventFromNsMbg
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
@ -33,7 +33,7 @@ class NSClientMbgWorker(
override fun doWork(): Result {
var ret = Result.success()
val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT
val acceptNSData = sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT
if (!acceptNSData) return ret
val mbgArray = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))

View file

@ -1,320 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.text.Spanned;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.interfaces.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventChargingState;
import info.nightscout.androidaps.events.EventNetworkChange;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI;
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.HtmlHelper;
import info.nightscout.androidaps.utils.ToastUtils;
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.disposables.CompositeDisposable;
@Singleton
public class NSClientPlugin extends PluginBase {
private final CompositeDisposable disposable = new CompositeDisposable();
private final AAPSLogger aapsLogger;
private final RxBusWrapper rxBus;
private final ResourceHelper resourceHelper;
private final Context context;
private final AapsSchedulers aapsSchedulers;
private final FabricPrivacy fabricPrivacy;
private final SP sp;
private final NsClientReceiverDelegate nsClientReceiverDelegate;
private final Config config;
private final BuildHelper buildHelper;
public Handler handler;
private final List<EventNSClientNewLog> listLog = new ArrayList<>();
Spanned textLog = HtmlHelper.INSTANCE.fromHtml("");
public boolean paused;
boolean autoscroll;
public String status = "";
public @Nullable NSClientService nsClientService = null;
@Inject
public NSClientPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
AapsSchedulers aapsSchedulers,
RxBusWrapper rxBus,
ResourceHelper resourceHelper,
Context context,
FabricPrivacy fabricPrivacy,
SP sp,
NsClientReceiverDelegate nsClientReceiverDelegate,
Config config,
BuildHelper buildHelper
) {
super(new PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(NSClientFragment.class.getName())
.pluginIcon(R.drawable.ic_nightscout_syncs)
.pluginName(R.string.nsclientinternal)
.shortName(R.string.nsclientinternal_shortname)
.preferencesId(R.xml.pref_nsclientinternal)
.description(R.string.description_ns_client),
aapsLogger, resourceHelper, injector
);
this.aapsLogger = aapsLogger;
this.aapsSchedulers = aapsSchedulers;
this.rxBus = rxBus;
this.resourceHelper = resourceHelper;
this.context = context;
this.fabricPrivacy = fabricPrivacy;
this.sp = sp;
this.nsClientReceiverDelegate = nsClientReceiverDelegate;
this.config = config;
this.buildHelper = buildHelper;
if (config.getNSCLIENT()) {
getPluginDescription().alwaysEnabled(true).visibleByDefault(true);
}
if (handler == null) {
HandlerThread handlerThread = new HandlerThread(NSClientPlugin.class.getSimpleName() + "Handler");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
}
public boolean isAllowed() {
return nsClientReceiverDelegate.allowed;
}
@Override
protected void onStart() {
paused = sp.getBoolean(R.string.key_nsclientinternal_paused, false);
autoscroll = sp.getBoolean(R.string.key_nsclientinternal_autoscroll, true);
Intent intent = new Intent(context, NSClientService.class);
context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
super.onStart();
nsClientReceiverDelegate.grabReceiversState();
disposable.add(rxBus
.toObservable(EventNSClientStatus.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
status = event.getStatus(resourceHelper);
rxBus.send(new EventNSClientUpdateGUI());
}, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventNetworkChange.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventPreferenceChange.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventAppExit.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
if (nsClientService != null) {
context.unbindService(mConnection);
}
}, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventNSClientNewLog.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
addToLog(event);
aapsLogger.debug(LTag.NSCLIENT, event.getAction() + " " + event.getLogText());
}, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventChargingState.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventNSClientResend.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> resend(event.getReason()), fabricPrivacy::logException)
);
}
@Override
protected void onStop() {
context.getApplicationContext().unbindService(mConnection);
disposable.clear();
super.onStop();
}
@Override
public void preprocessPreferences(@NonNull PreferenceFragmentCompat preferenceFragment) {
super.preprocessPreferences(preferenceFragment);
if (config.getNSCLIENT()) {
SwitchPreference key_ns_uploadlocalprofile = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_uploadlocalprofile));
if (key_ns_uploadlocalprofile != null) key_ns_uploadlocalprofile.setVisible(false);
SwitchPreference key_ns_autobackfill = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_autobackfill));
if (key_ns_autobackfill != null) key_ns_autobackfill.setVisible(false);
SwitchPreference key_ns_create_announcements_from_errors = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_create_announcements_from_errors));
if (key_ns_create_announcements_from_errors != null)
key_ns_create_announcements_from_errors.setVisible(false);
SwitchPreference key_ns_create_announcements_from_carbs_req = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_create_announcements_from_carbs_req));
if (key_ns_create_announcements_from_carbs_req != null)
key_ns_create_announcements_from_carbs_req.setVisible(false);
SwitchPreference key_ns_upload_only = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_upload_only));
if (key_ns_upload_only != null) {
key_ns_upload_only.setVisible(false);
key_ns_upload_only.setEnabled(false);
}
SwitchPreference key_ns_sync_use_absolute = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_sync_use_absolute));
if (key_ns_sync_use_absolute != null) key_ns_sync_use_absolute.setVisible(false);
} else {
// APS or pumpControl mode
SwitchPreference key_ns_upload_only = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_upload_only));
if (key_ns_upload_only != null)
key_ns_upload_only.setVisible(buildHelper.isEngineeringMode());
}
}
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
aapsLogger.debug(LTag.NSCLIENT, "Service is disconnected");
nsClientService = null;
}
public void onServiceConnected(ComponentName name, IBinder service) {
aapsLogger.debug(LTag.NSCLIENT, "Service is connected");
NSClientService.LocalBinder mLocalBinder = (NSClientService.LocalBinder) service;
if (mLocalBinder != null) // is null when running in roboelectric
nsClientService = mLocalBinder.getServiceInstance();
}
};
synchronized void clearLog() {
handler.post(() -> {
synchronized (listLog) {
listLog.clear();
}
rxBus.send(new EventNSClientUpdateGUI());
});
}
private synchronized void addToLog(final EventNSClientNewLog ev) {
handler.post(() -> {
synchronized (listLog) {
listLog.add(ev);
// remove the first line if log is too large
if (listLog.size() >= Constants.MAX_LOG_LINES) {
listLog.remove(0);
}
}
rxBus.send(new EventNSClientUpdateGUI());
});
}
synchronized void updateLog() {
try {
StringBuilder newTextLog = new StringBuilder();
synchronized (listLog) {
for (EventNSClientNewLog log : listLog) {
newTextLog.append(log.toPreparedHtml());
}
}
textLog = HtmlHelper.INSTANCE.fromHtml(newTextLog.toString());
} catch (OutOfMemoryError e) {
ToastUtils.showToastInUiThread(context, rxBus, "Out of memory!\nStop using this phone !!!", R.raw.error);
}
}
void resend(String reason) {
if (nsClientService != null)
nsClientService.resend(reason);
}
public void pause(boolean newState) {
sp.putBoolean(R.string.key_nsclientinternal_paused, newState);
paused = newState;
rxBus.send(new EventPreferenceChange(resourceHelper, R.string.key_nsclientinternal_paused));
}
public String url() {
return NSClientService.nsURL;
}
public boolean hasWritePermission() {
return NSClientService.hasWriteAuth;
}
public void handleClearAlarm(NSAlarm originalAlarm, long silenceTimeInMsec) {
if (!isEnabled(PluginType.GENERAL)) {
return;
}
if (sp.getBoolean(R.string.key_ns_noupload, false)) {
aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped");
return;
}
AlarmAck ack = new AlarmAck();
ack.level = originalAlarm.level();
ack.group = originalAlarm.group();
ack.silenceTime = silenceTimeInMsec;
if (nsClientService != null)
nsClientService.sendAlarmAck(ack);
}
public void updateLatestDateReceivedIfNewer(long latestReceived) {
if (nsClientService != null && latestReceived > nsClientService.latestDateInReceivedData)
nsClientService.latestDateInReceivedData = latestReceived;
}
}

View file

@ -0,0 +1,243 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Handler
import android.os.HandlerThread
import android.os.IBinder
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventChargingState
import info.nightscout.androidaps.events.EventNetworkChange
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck
import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper.fromHtml
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NSClientPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
private val aapsSchedulers: AapsSchedulers,
private val rxBus: RxBusWrapper,
resourceHelper: ResourceHelper,
private val context: Context,
private val fabricPrivacy: FabricPrivacy,
private val sp: SP,
private val nsClientReceiverDelegate: NsClientReceiverDelegate,
private val config: Config,
private val buildHelper: BuildHelper
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(NSClientFragment::class.java.name)
.pluginIcon(R.drawable.ic_nightscout_syncs)
.pluginName(R.string.nsclientinternal)
.shortName(R.string.nsclientinternal_shortname)
.preferencesId(R.xml.pref_nsclientinternal)
.description(R.string.description_ns_client),
aapsLogger, resourceHelper, injector
) {
private val disposable = CompositeDisposable()
var handler: Handler? = null
private val listLog: MutableList<EventNSClientNewLog> = ArrayList()
var textLog = fromHtml("")
var paused = false
var autoscroll = false
var status = ""
var nsClientService: NSClientService? = null
val isAllowed: Boolean
get() = nsClientReceiverDelegate.allowed
init {
if (config.NSCLIENT) {
pluginDescription.alwaysEnabled(true).visibleByDefault(true)
}
if (handler == null) {
val handlerThread = HandlerThread(NSClientPlugin::class.java.simpleName + "Handler")
handlerThread.start()
handler = Handler(handlerThread.looper)
}
}
override fun onStart() {
paused = sp.getBoolean(R.string.key_nsclientinternal_paused, false)
autoscroll = sp.getBoolean(R.string.key_nsclientinternal_autoscroll, true)
val intent = Intent(context, NSClientService::class.java)
context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
super.onStart()
nsClientReceiverDelegate.grabReceiversState()
disposable.add(rxBus
.toObservable(EventNSClientStatus::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventNSClientStatus ->
status = event.getStatus(resourceHelper)
rxBus.send(EventNSClientUpdateGUI())
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventNetworkChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ if (nsClientService != null) context.unbindService(mConnection) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventNSClientNewLog::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventNSClientNewLog ->
addToLog(event)
aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText)
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventChargingState::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventNSClientResend::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException)
)
}
override fun onStop() {
context.applicationContext.unbindService(mConnection)
disposable.clear()
super.onStop()
}
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
super.preprocessPreferences(preferenceFragment)
if (config.NSCLIENT) {
preferenceFragment.findPreference<PreferenceScreen>(resourceHelper.gs(R.string.ns_sync_options))?.isVisible = false
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_ns_create_announcements_from_errors))?.isVisible = false
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_ns_create_announcements_from_carbs_req))?.isVisible = false
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_ns_sync_use_absolute))?.isVisible = false
} else {
// APS or pumpControl mode
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_ns_receive_profile_switch))?.isVisible = buildHelper.isEngineeringMode()
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_ns_receive_insulin))?.isVisible = buildHelper.isEngineeringMode()
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_ns_receive_carbs))?.isVisible = buildHelper.isEngineeringMode()
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_ns_receive_temp_target))?.isVisible = buildHelper.isEngineeringMode()
}
}
private val mConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
aapsLogger.debug(LTag.NSCLIENT, "Service is disconnected")
nsClientService = null
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
aapsLogger.debug(LTag.NSCLIENT, "Service is connected")
val mLocalBinder = service as NSClientService.LocalBinder
@Suppress("UNNECESSARY_SAFE_CALL")
nsClientService = mLocalBinder?.serviceInstance // is null when running in roboelectric
}
}
@Synchronized fun clearLog() {
handler?.post {
synchronized(listLog) { listLog.clear() }
rxBus.send(EventNSClientUpdateGUI())
}
}
@Synchronized private fun addToLog(ev: EventNSClientNewLog) {
handler?.post {
synchronized(listLog) {
listLog.add(ev)
// remove the first line if log is too large
if (listLog.size >= Constants.MAX_LOG_LINES) {
listLog.removeAt(0)
}
}
rxBus.send(EventNSClientUpdateGUI())
}
}
@Synchronized fun updateLog() {
try {
val newTextLog = StringBuilder()
synchronized(listLog) {
for (log in listLog) {
newTextLog.append(log.toPreparedHtml())
}
}
textLog = fromHtml(newTextLog.toString())
} catch (e: OutOfMemoryError) {
ToastUtils.showToastInUiThread(context, rxBus, "Out of memory!\nStop using this phone !!!", R.raw.error)
}
}
fun resend(reason: String) {
nsClientService?.resend(reason)
}
fun pause(newState: Boolean) {
sp.putBoolean(R.string.key_nsclientinternal_paused, newState)
paused = newState
rxBus.send(EventPreferenceChange(resourceHelper, R.string.key_nsclientinternal_paused))
}
fun url(): String = nsClientService?.nsURL ?: ""
fun hasWritePermission(): Boolean = nsClientService?.hasWriteAuth ?: false
fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long) {
if (!isEnabled(PluginType.GENERAL)) return
if (!sp.getBoolean(R.string.key_ns_upload, false)) {
aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped")
return
}
nsClientService?.sendAlarmAck(
AlarmAck().also { ack ->
ack.level = originalAlarm.level()
ack.group = originalAlarm.group()
ack.silenceTime = silenceTimeInMilliseconds
})
}
fun updateLatestDateReceivedIfNewer(latestReceived: Long) {
nsClientService?.let { if (latestReceived > it.latestDateInReceivedData) it.latestDateInReceivedData = latestReceived }
}
}

View file

@ -42,8 +42,9 @@ class NSClientRemoveWorker(
@Inject lateinit var uel: UserEntryLogger
override fun doWork(): Result {
val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT
if (!acceptNSData) return Result.success()
// Do not accept removed data over WS. Only invalidated trough NSClient
@Suppress("ConstantConditionIf")
if (true) return Result.success()
var ret = Result.success()

View file

@ -1,117 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import java.util.Arrays;
import javax.inject.Inject;
import javax.inject.Singleton;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventChargingState;
import info.nightscout.androidaps.events.EventNetworkChange;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.receivers.ReceiverStatusStore;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@Singleton
class NsClientReceiverDelegate {
private boolean allowedChargingState = true;
private boolean allowedNetworkState = true;
boolean allowed = true;
private RxBusWrapper rxBus;
private ResourceHelper resourceHelper;
private SP sp;
private ReceiverStatusStore receiverStatusStore;
@Inject
public NsClientReceiverDelegate(
RxBusWrapper rxBus,
ResourceHelper resourceHelper,
SP sp,
ReceiverStatusStore receiverStatusStore
) {
this.rxBus = rxBus;
this.resourceHelper = resourceHelper;
this.sp = sp;
this.receiverStatusStore = receiverStatusStore;
}
void grabReceiversState() {
receiverStatusStore.updateNetworkStatus();
}
void onStatusEvent(EventPreferenceChange ev) {
if (ev.isChanged(resourceHelper, R.string.key_ns_wifionly) ||
ev.isChanged(resourceHelper, R.string.key_ns_wifi_ssids) ||
ev.isChanged(resourceHelper, R.string.key_ns_allowroaming)
) {
receiverStatusStore.updateNetworkStatus();
onStatusEvent(receiverStatusStore.getLastNetworkEvent());
} else if (ev.isChanged(resourceHelper, R.string.key_ns_chargingonly)) {
receiverStatusStore.broadcastChargingState();
}
}
void onStatusEvent(final EventChargingState ev) {
boolean newChargingState = calculateStatus(ev);
if (newChargingState != allowedChargingState) {
allowedChargingState = newChargingState;
processStateChange();
}
}
void onStatusEvent(final EventNetworkChange ev) {
boolean newNetworkState = calculateStatus(ev);
if (newNetworkState != allowedNetworkState) {
allowedNetworkState = newNetworkState;
processStateChange();
}
}
private void processStateChange() {
boolean newAllowedState = allowedChargingState && allowedNetworkState;
if (newAllowedState != allowed) {
allowed = newAllowedState;
rxBus.send(new EventPreferenceChange(resourceHelper.gs(R.string.key_nsclientinternal_paused)));
}
}
boolean calculateStatus(final EventChargingState ev) {
boolean chargingOnly = sp.getBoolean(R.string.key_ns_chargingonly, false);
boolean newAllowedState = true;
if (!ev.isCharging() && chargingOnly) {
newAllowedState = false;
}
return newAllowedState;
}
boolean calculateStatus(final EventNetworkChange ev) {
boolean wifiOnly = sp.getBoolean(R.string.key_ns_wifionly, false);
String allowedSSIDstring = sp.getString(R.string.key_ns_wifi_ssids, "");
String[] allowedSSIDs = allowedSSIDstring.split(";");
if (allowedSSIDstring.isEmpty()) allowedSSIDs = new String[0];
boolean allowRoaming = sp.getBoolean(R.string.key_ns_allowroaming, true);
boolean newAllowedState = true;
if (ev.getWifiConnected()) {
if (allowedSSIDs.length != 0 && !Arrays.asList(allowedSSIDs).contains(ev.getSsid())) {
newAllowedState = false;
}
} else {
if ((!allowRoaming && ev.getRoaming()) || wifiOnly) {
newAllowedState = false;
}
}
return newAllowedState;
}
}

View file

@ -0,0 +1,91 @@
package info.nightscout.androidaps.plugins.general.nsclient
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventChargingState
import info.nightscout.androidaps.events.EventNetworkChange
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.receivers.ReceiverStatusStore
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NsClientReceiverDelegate @Inject constructor(
private val rxBus: RxBusWrapper,
private val resourceHelper: ResourceHelper,
private val sp: SP,
private val receiverStatusStore: ReceiverStatusStore
) {
private var allowedChargingState = true
private var allowedNetworkState = true
var allowed = true
fun grabReceiversState() {
receiverStatusStore.updateNetworkStatus()
}
fun onStatusEvent(ev: EventPreferenceChange) {
if (ev.isChanged(resourceHelper, R.string.key_ns_wifionly) ||
ev.isChanged(resourceHelper, R.string.key_ns_wifi_ssids) ||
ev.isChanged(resourceHelper, R.string.key_ns_allowroaming)) {
receiverStatusStore.updateNetworkStatus()
onStatusEvent(receiverStatusStore.lastNetworkEvent)
} else if (ev.isChanged(resourceHelper, R.string.key_ns_chargingonly)) {
receiverStatusStore.broadcastChargingState()
}
}
fun onStatusEvent(ev: EventChargingState) {
val newChargingState = calculateStatus(ev)
if (newChargingState != allowedChargingState) {
allowedChargingState = newChargingState
processStateChange()
}
}
fun onStatusEvent(ev: EventNetworkChange?) {
val newNetworkState = calculateStatus(ev)
if (newNetworkState != allowedNetworkState) {
allowedNetworkState = newNetworkState
processStateChange()
}
}
private fun processStateChange() {
val newAllowedState = allowedChargingState && allowedNetworkState
if (newAllowedState != allowed) {
allowed = newAllowedState
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.key_nsclientinternal_paused)))
}
}
fun calculateStatus(ev: EventChargingState): Boolean {
val chargingOnly = sp.getBoolean(R.string.key_ns_chargingonly, false)
var newAllowedState = true
if (!ev.isCharging && chargingOnly) {
newAllowedState = false
}
return newAllowedState
}
fun calculateStatus(ev: EventNetworkChange?): Boolean {
val wifiOnly = sp.getBoolean(R.string.key_ns_wifionly, false)
val allowedSsidString = sp.getString(R.string.key_ns_wifi_ssids, "")
val allowedSSIDs: List<String> = if (allowedSsidString.isEmpty()) List(0) { "" } else allowedSsidString.split(";")
val allowRoaming = sp.getBoolean(R.string.key_ns_allowroaming, true)
var newAllowedState = true
if (ev?.wifiConnected == true) {
if (allowedSSIDs.isNotEmpty() && !allowedSSIDs.contains(ev.ssid)) {
newAllowedState = false
}
} else {
if (!allowRoaming && ev?.roaming == true || wifiOnly) {
newAllowedState = false
}
}
return newAllowedState
}
}

View file

@ -1,124 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import com.j256.ormlite.dao.CloseableIterator;
import org.json.JSONException;
import org.json.JSONObject;
import java.sql.SQLException;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.DbRequest;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.interfaces.UploadQueueAdminInterface;
import info.nightscout.androidaps.interfaces.UploadQueueInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend;
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
/**
* Created by mike on 21.02.2016.
*/
public class UploadQueue implements UploadQueueAdminInterface {
private final AAPSLogger aapsLogger;
private final DatabaseHelperInterface databaseHelper;
private final Context context;
private final SP sp;
private final RxBusWrapper rxBus;
public UploadQueue(
AAPSLogger aapsLogger,
DatabaseHelperInterface databaseHelper,
Context context,
SP sp,
RxBusWrapper rxBus
) {
this.aapsLogger = aapsLogger;
this.databaseHelper = databaseHelper;
this.context = context;
this.sp = sp;
this.rxBus = rxBus;
}
public String status() {
return "QUEUE: " + databaseHelper.size(DatabaseHelper.DATABASE_DBREQUESTS);
}
@Override
public long size() {
return databaseHelper.size(DatabaseHelper.DATABASE_DBREQUESTS);
}
private void startService() {
if (NSClientService.handler == null) {
context.startService(new Intent(context, NSClientService.class));
SystemClock.sleep(2000);
}
}
public void add(final DbRequest dbr) {
if (sp.getBoolean(R.string.key_ns_noupload, false)) return;
aapsLogger.debug(LTag.NSCLIENT, "Adding to queue: " + dbr.log());
try {
databaseHelper.create(dbr);
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
rxBus.send(new EventNSClientResend("newdata"));
}
@Override public void clearQueue() {
startService();
if (NSClientService.handler != null) {
NSClientService.handler.post(() -> {
aapsLogger.debug(LTag.NSCLIENT, "ClearQueue");
databaseHelper.deleteAllDbRequests();
aapsLogger.debug(LTag.NSCLIENT, status());
});
}
}
@Override
public void removeByMongoId(final String action, final String _id) {
if (_id == null || _id.equals(""))
return;
startService();
if (NSClientService.handler != null) {
NSClientService.handler.post(() -> {
databaseHelper.deleteDbRequestbyMongoId(action, _id);
aapsLogger.debug(LTag.NSCLIENT, "Removing " + _id + " from UploadQueue. " + status());
});
}
}
@Override public String textList() {
String result = "";
CloseableIterator<DbRequest> iterator;
try {
iterator = databaseHelper.getDbRequestIterator();
try {
while (iterator.hasNext()) {
DbRequest dbr = iterator.next();
result += "<br>";
result += dbr.action.toUpperCase() + " ";
result += dbr.collection + ": ";
result += dbr.data;
}
} finally {
iterator.close();
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return result;
}
}

View file

@ -1,913 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient.services;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.SystemClock;
import androidx.work.OneTimeWorkRequest;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import com.j256.ormlite.dao.CloseableIterator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import dagger.android.DaggerService;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.interfaces.Config;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.database.AppRepository;
import info.nightscout.androidaps.db.DbRequest;
import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventConfigBuilderChange;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.interfaces.DataSyncSelector;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.UploadQueueInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.food.FoodPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker;
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck;
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAuthAck;
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck;
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction;
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin;
import info.nightscout.androidaps.receivers.DataWorker;
import info.nightscout.androidaps.services.Intents;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.disposables.CompositeDisposable;
import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;
public class NSClientService extends DaggerService {
@Inject HasAndroidInjector injector;
@Inject AAPSLogger aapsLogger;
@Inject AapsSchedulers aapsSchedulers;
@Inject NSSettingsStatus nsSettingsStatus;
@Inject NSDeviceStatus nsDeviceStatus;
@Inject DatabaseHelperInterface databaseHelper;
@Inject RxBusWrapper rxBus;
@Inject ResourceHelper resourceHelper;
@Inject SP sp;
@Inject FabricPrivacy fabricPrivacy;
@Inject NSClientPlugin nsClientPlugin;
@Inject BuildHelper buildHelper;
@Inject Config config;
@Inject DateUtil dateUtil;
@Inject UploadQueueInterface uploadQueue;
@Inject DataWorker dataWorker;
@Inject DataSyncSelector dataSyncSelector;
@Inject AppRepository repository;
private final CompositeDisposable disposable = new CompositeDisposable();
static public PowerManager.WakeLock mWakeLock;
private final IBinder mBinder = new NSClientService.LocalBinder();
static public Handler handler;
public static Socket mSocket;
public static boolean isConnected = false;
public static boolean hasWriteAuth = false;
private static Integer dataCounter = 0;
private static Integer connectCounter = 0;
private boolean nsEnabled = false;
static public String nsURL = "";
private String nsAPISecret = "";
private String nsDevice = "";
private final Integer nsHours = 48;
public long lastResendTime = 0;
public long lastAckTime = 0;
public long latestDateInReceivedData = 0;
private String nsAPIhashCode = "";
private final ArrayList<Long> reconnections = new ArrayList<>();
private final int WATCHDOG_INTERVAL_MINUTES = 2;
private final int WATCHDOG_RECONNECT_IN = 15;
private final int WATCHDOG_MAX_CONNECTIONS = 5;
public NSClientService() {
super();
if (handler == null) {
HandlerThread handlerThread = new HandlerThread(NSClientService.class.getSimpleName() + "Handler");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
}
@Override
public void onCreate() {
super.onCreate();
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService");
mWakeLock.acquire();
initialize();
disposable.add(rxBus
.toObservable(EventConfigBuilderChange.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
if (nsEnabled != nsClientPlugin.isEnabled(PluginType.GENERAL)) {
latestDateInReceivedData = 0;
destroy();
initialize();
}
}, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventPreferenceChange.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
if (event.isChanged(resourceHelper, R.string.key_nsclientinternal_url) ||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_api_secret) ||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_paused)
) {
latestDateInReceivedData = 0;
destroy();
initialize();
}
}, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventAppExit.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
aapsLogger.debug(LTag.NSCLIENT, "EventAppExit received");
destroy();
stopSelf();
}, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(EventNSClientRestart.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
latestDateInReceivedData = 0;
restart();
}, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(NSAuthAck.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(this::processAuthAck, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(NSUpdateAck.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(this::processUpdateAck, fabricPrivacy::logException)
);
disposable.add(rxBus
.toObservable(NSAddAck.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(this::processAddAck, fabricPrivacy::logException)
);
}
@Override
public void onDestroy() {
super.onDestroy();
disposable.clear();
if (mWakeLock.isHeld()) mWakeLock.release();
}
public void processAddAck(NSAddAck ack) {
lastAckTime = dateUtil.now();
dataWorker.enqueue(
new OneTimeWorkRequest.Builder(NSClientAddAckWorker.class)
.setInputData(dataWorker.storeInputData(ack, null))
.build());
}
public void processUpdateAck(NSUpdateAck ack) {
lastAckTime = dateUtil.now();
dataWorker.enqueue(
new OneTimeWorkRequest.Builder(NSClientUpdateRemoveAckWorker.class)
.setInputData(dataWorker.storeInputData(ack, null))
.build());
}
public void processAuthAck(NSAuthAck ack) {
String connectionStatus = "Authenticated (";
if (ack.read) connectionStatus += "R";
if (ack.write) connectionStatus += "W";
if (ack.write_treatment) connectionStatus += "T";
connectionStatus += ')';
isConnected = true;
hasWriteAuth = ack.write && ack.write_treatment;
rxBus.send(new EventNSClientStatus(connectionStatus));
rxBus.send(new EventNSClientNewLog("AUTH", connectionStatus));
if (!ack.write) {
rxBus.send(new EventNSClientNewLog("ERROR", "Write permission not granted !!!!"));
}
if (!ack.write_treatment) {
rxBus.send(new EventNSClientNewLog("ERROR", "Write treatment permission not granted !!!!"));
}
if (!hasWriteAuth) {
Notification noperm = new Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, resourceHelper.gs(R.string.nowritepermission), Notification.URGENT);
rxBus.send(new EventNewNotification(noperm));
} else {
rxBus.send(new EventDismissNotification(Notification.NSCLIENT_NO_WRITE_PERMISSION));
}
}
public class LocalBinder extends Binder {
public NSClientService getServiceInstance() {
return NSClientService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@SuppressWarnings("deprecation")
public void initialize() {
dataCounter = 0;
readPreferences();
if (!nsAPISecret.equals(""))
nsAPIhashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString();
rxBus.send(new EventNSClientStatus("Initializing"));
if (!nsClientPlugin.isAllowed()) {
rxBus.send(new EventNSClientNewLog("NSCLIENT", "not allowed"));
rxBus.send(new EventNSClientStatus("Not allowed"));
} else if (nsClientPlugin.paused) {
rxBus.send(new EventNSClientNewLog("NSCLIENT", "paused"));
rxBus.send(new EventNSClientStatus("Paused"));
} else if (!nsEnabled) {
rxBus.send(new EventNSClientNewLog("NSCLIENT", "disabled"));
rxBus.send(new EventNSClientStatus("Disabled"));
} else if (!nsURL.equals("") && (buildHelper.isEngineeringMode() || nsURL.toLowerCase().startsWith("https://"))) {
try {
rxBus.send(new EventNSClientStatus("Connecting ..."));
IO.Options opt = new IO.Options();
opt.forceNew = true;
opt.reconnection = true;
mSocket = IO.socket(nsURL, opt);
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.on(Socket.EVENT_ERROR, onError);
mSocket.on(Socket.EVENT_CONNECT_ERROR, onError);
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onError);
mSocket.on(Socket.EVENT_PING, onPing);
rxBus.send(new EventNSClientNewLog("NSCLIENT", "do connect"));
mSocket.connect();
mSocket.on("dataUpdate", onDataUpdate);
mSocket.on("announcement", onAnnouncement);
mSocket.on("alarm", onAlarm);
mSocket.on("urgent_alarm", onUrgentAlarm);
mSocket.on("clear_alarm", onClearAlarm);
} catch (URISyntaxException | RuntimeException e) {
rxBus.send(new EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"));
rxBus.send(new EventNSClientStatus("Wrong URL syntax"));
}
} else if (nsURL.toLowerCase().startsWith("http://")) {
rxBus.send(new EventNSClientNewLog("NSCLIENT", "NS URL not encrypted"));
rxBus.send(new EventNSClientStatus("Not encrypted"));
} else {
rxBus.send(new EventNSClientNewLog("NSCLIENT", "No NS URL specified"));
rxBus.send(new EventNSClientStatus("Not configured"));
}
}
private final Emitter.Listener onConnect = new Emitter.Listener() {
@Override
public void call(Object... args) {
connectCounter++;
String socketId = mSocket != null ? mSocket.id() : "NULL";
rxBus.send(new EventNSClientNewLog("NSCLIENT", "connect #" + connectCounter + " event. ID: " + socketId));
if (mSocket != null)
sendAuthMessage(new NSAuthAck(rxBus));
watchdog();
}
};
void watchdog() {
synchronized (reconnections) {
long now = dateUtil.now();
reconnections.add(now);
for (int i = 0; i < reconnections.size(); i++) {
Long r = reconnections.get(i);
if (r < now - T.mins(WATCHDOG_INTERVAL_MINUTES).msecs()) {
reconnections.remove(r);
}
}
rxBus.send(new EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " mins: " + reconnections.size() + "/" + WATCHDOG_MAX_CONNECTIONS));
if (reconnections.size() >= WATCHDOG_MAX_CONNECTIONS) {
Notification n = new Notification(Notification.NS_MALFUNCTION, resourceHelper.gs(R.string.nsmalfunction), Notification.URGENT);
rxBus.send(new EventNewNotification(n));
rxBus.send(new EventNSClientNewLog("WATCHDOG", "pausing for " + WATCHDOG_RECONNECT_IN + " mins"));
nsClientPlugin.pause(true);
rxBus.send(new EventNSClientUpdateGUI());
new Thread(() -> {
SystemClock.sleep(T.mins(WATCHDOG_RECONNECT_IN).msecs());
rxBus.send(new EventNSClientNewLog("WATCHDOG", "reenabling NSClient"));
nsClientPlugin.pause(false);
}).start();
}
}
}
private final Emitter.Listener onDisconnect = new Emitter.Listener() {
@Override
public void call(Object... args) {
aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", args);
rxBus.send(new EventNSClientNewLog("NSCLIENT", "disconnect event"));
}
};
public synchronized void destroy() {
if (mSocket != null) {
mSocket.off(Socket.EVENT_CONNECT);
mSocket.off(Socket.EVENT_DISCONNECT);
mSocket.off(Socket.EVENT_PING);
mSocket.off("dataUpdate");
mSocket.off("announcement");
mSocket.off("alarm");
mSocket.off("urgent_alarm");
mSocket.off("clear_alarm");
rxBus.send(new EventNSClientNewLog("NSCLIENT", "destroy"));
isConnected = false;
hasWriteAuth = false;
mSocket.disconnect();
mSocket = null;
}
}
public void sendAuthMessage(NSAuthAck ack) {
JSONObject authMessage = new JSONObject();
try {
authMessage.put("client", "Android_" + nsDevice);
authMessage.put("history", nsHours);
authMessage.put("status", true); // receive status
authMessage.put("from", latestDateInReceivedData); // send data newer than
authMessage.put("secret", nsAPIhashCode);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
return;
}
rxBus.send(new EventNSClientNewLog("AUTH", "requesting auth"));
if (mSocket != null)
mSocket.emit("authorize", authMessage, ack);
}
public void readPreferences() {
nsEnabled = nsClientPlugin.isEnabled(PluginType.GENERAL);
nsURL = sp.getString(R.string.key_nsclientinternal_url, "");
nsAPISecret = sp.getString(R.string.key_nsclientinternal_api_secret, "");
nsDevice = sp.getString("careportal_enteredby", "");
}
private final Emitter.Listener onError = new Emitter.Listener() {
@Override
public void call(final Object... args) {
String msg = "Unknown Error";
if (args.length > 0 && args[0] != null) {
msg = args[0].toString();
}
rxBus.send(new EventNSClientNewLog("ERROR", msg));
}
};
private final Emitter.Listener onPing = new Emitter.Listener() {
@Override
public void call(final Object... args) {
rxBus.send(new EventNSClientNewLog("PING", "received"));
// send data if there is something waiting
resend("Ping received");
}
};
private final Emitter.Listener onAnnouncement = new Emitter.Listener() {
/*
{
"level":0,
"title":"Announcement",
"message":"test",
"plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true},
"group":"Announcement",
"isAnnouncement":true,
"key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da"
}
*/
@Override
public void call(final Object... args) {
JSONObject data;
try {
data = (JSONObject) args[0];
handleAnnouncement(data);
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
}
};
private final Emitter.Listener onAlarm = new Emitter.Listener() {
/*
{
"level":1,
"title":"Warning HIGH",
"message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g",
"eventName":"high",
"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true},
"pushoverSound":"climb",
"debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}},
"group":"default",
"key":"simplealarms_1"
}
*/
@Override
public void call(final Object... args) {
JSONObject data;
try {
data = (JSONObject) args[0];
handleAlarm(data);
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
}
};
private final Emitter.Listener onUrgentAlarm = args -> {
JSONObject data;
try {
data = (JSONObject) args[0];
handleUrgentAlarm(data);
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
};
private final Emitter.Listener onClearAlarm = new Emitter.Listener() {
/*
{
"clear":true,
"title":"All Clear",
"message":"default - Urgent was ack'd",
"group":"default"
}
*/
@Override
public void call(final Object... args) {
JSONObject data;
try {
data = (JSONObject) args[0];
rxBus.send(new EventNSClientNewLog("CLEARALARM", "received"));
rxBus.send(new EventDismissNotification(Notification.NS_ALARM));
rxBus.send(new EventDismissNotification(Notification.NS_URGENT_ALARM));
aapsLogger.debug(LTag.NSCLIENT, data.toString());
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
}
};
private final Emitter.Listener onDataUpdate = new Emitter.Listener() {
@Override
public void call(final Object... args) {
NSClientService.handler.post(() -> {
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"AndroidAPS:NSClientService_onDataUpdate");
wakeLock.acquire();
try {
JSONObject data = (JSONObject) args[0];
boolean broadcastProfile = false;
try {
// delta means only increment/changes are comming
boolean isDelta = data.has("delta");
boolean isFull = !isDelta;
rxBus.send(new EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + (isDelta ? " delta" : " full")));
if (data.has("status")) {
JSONObject status = data.getJSONObject("status");
nsSettingsStatus.handleNewData(status);
} else if (!isDelta) {
rxBus.send(new EventNSClientNewLog("ERROR", "Unsupported Nightscout version !!!!"));
}
if (data.has("profiles")) {
JSONArray profiles = data.getJSONArray("profiles");
if (profiles.length() > 0) {
// take the newest
JSONObject profileStoreJson = (JSONObject) profiles.get(profiles.length() - 1);
rxBus.send(new EventNSClientNewLog("PROFILE", "profile received"));
dataWorker.enqueue(
new OneTimeWorkRequest.Builder(NSProfilePlugin.NSProfileWorker.class)
.setInputData(dataWorker.storeInputData(profileStoreJson, null))
.build());
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
Bundle bundle = new Bundle();
bundle.putString("profile", profileStoreJson.toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_NEW_PROFILE);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
}
}
}
if (data.has("treatments")) {
JSONArray treatments = data.getJSONArray("treatments");
JSONArray removedTreatments = new JSONArray();
JSONArray addedOrUpdatedTreatments = new JSONArray();
if (treatments.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments"));
for (Integer index = 0; index < treatments.length(); index++) {
JSONObject jsonTreatment = treatments.getJSONObject(index);
String action = JsonHelper.safeGetStringAllowNull(jsonTreatment, "action", null);
long mills = JsonHelper.safeGetLong(jsonTreatment, "mills");
if (action == null) addedOrUpdatedTreatments.put(jsonTreatment);
else if (action.equals("update"))
addedOrUpdatedTreatments.put(jsonTreatment);
else if (action.equals("remove") && mills > dateUtil.now() - T.days(1).msecs()) // handle 1 day old deletions only
removedTreatments.put(jsonTreatment);
}
if (removedTreatments.length() > 0) {
dataWorker.enqueue(
new OneTimeWorkRequest.Builder(NSClientRemoveWorker.class)
.setInputData(dataWorker.storeInputData(removedTreatments, null))
.build());
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
Bundle bundle = new Bundle();
bundle.putString("treatments", removedTreatments.toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_REMOVED_TREATMENT);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
}
}
if (addedOrUpdatedTreatments.length() > 0) {
dataWorker.enqueue(
new OneTimeWorkRequest.Builder(NSClientAddUpdateWorker.class)
.setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null))
.build());
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
List<JSONArray> splitted = splitArray(addedOrUpdatedTreatments);
for (JSONArray part : splitted) {
Bundle bundle = new Bundle();
bundle.putString("treatments", part.toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_CHANGED_TREATMENT);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
}
}
}
}
if (data.has("devicestatus")) {
JSONArray devicestatuses = data.getJSONArray("devicestatus");
if (devicestatuses.length() > 0) {
rxBus.send(new EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses"));
nsDeviceStatus.handleNewData(devicestatuses);
}
}
if (data.has("food")) {
JSONArray foods = data.getJSONArray("food");
if (foods.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods"));
dataWorker.enqueue(
new OneTimeWorkRequest.Builder(FoodPlugin.FoodWorker.class)
.setInputData(dataWorker.storeInputData(foods, null))
.build());
}
//noinspection SpellCheckingInspection
if (data.has("mbgs")) {
JSONArray mbgArray = data.getJSONArray("mbgs");
if (mbgArray.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs"));
dataWorker.enqueue(
new OneTimeWorkRequest.Builder(NSClientMbgWorker.class)
.setInputData(dataWorker.storeInputData(mbgArray, null))
.build());
}
if (data.has("cals")) {
JSONArray cals = data.getJSONArray("cals");
if (cals.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + cals.length() + " cals"));
// Calibrations ignored
}
if (data.has("sgvs")) {
JSONArray sgvs = data.getJSONArray("sgvs");
if (sgvs.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs"));
dataWorker.enqueue(new OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker.class)
.setInputData(dataWorker.storeInputData(sgvs, null))
.build());
List<JSONArray> splitted = splitArray(sgvs);
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
for (JSONArray part : splitted) {
Bundle bundle = new Bundle();
bundle.putString("sgvs", part.toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_NEW_SGV);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
}
}
}
rxBus.send(new EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
//rxBus.send(new EventNSClientNewLog("NSCLIENT", "onDataUpdate end");
} finally {
if (wakeLock.isHeld()) wakeLock.release();
}
});
}
};
public void dbUpdate(DbRequest dbr, NSUpdateAck ack) {
try {
if (!isConnected || !hasWriteAuth) return;
JSONObject message = new JSONObject();
message.put("collection", dbr.collection);
message.put("_id", dbr._id);
message.put("data", new JSONObject(dbr.data));
mSocket.emit("dbUpdate", message, ack);
rxBus.send(new EventNSClientNewLog("DBUPDATE " + dbr.collection, "Sent " + dbr._id));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void dbUpdate(String collection, String _id, JSONObject data, Object originalObject) {
try {
if (!isConnected || !hasWriteAuth) return;
JSONObject message = new JSONObject();
message.put("collection", collection);
message.put("_id", _id);
message.put("data", data);
mSocket.emit("dbUpdate", message, new NSUpdateAck("dbUpdate", _id, aapsLogger, rxBus, originalObject));
rxBus.send(new EventNSClientNewLog("DBUPDATE " + collection, "Sent " + originalObject.getClass().getSimpleName() + " " + _id));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void dbRemove(DbRequest dbr, NSUpdateAck ack) {
try {
if (!isConnected || !hasWriteAuth) return;
JSONObject message = new JSONObject();
message.put("collection", dbr.collection);
message.put("_id", dbr._id);
mSocket.emit("dbRemove", message, ack);
rxBus.send(new EventNSClientNewLog("DBREMOVE " + dbr.collection, "Sent " + dbr._id));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void dbRemove(String collection, String _id, Object originalObject) {
try {
if (!isConnected || !hasWriteAuth) return;
JSONObject message = new JSONObject();
message.put("collection", collection);
message.put("_id", _id);
mSocket.emit("dbRemove", message, new NSUpdateAck("dbRemove", _id, aapsLogger, rxBus, originalObject));
rxBus.send(new EventNSClientNewLog("DBREMOVE " + collection, "Sent " + originalObject.getClass().getSimpleName() + " " + _id));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void dbAdd(DbRequest dbr, NSAddAck ack) {
try {
if (!isConnected || !hasWriteAuth) return;
JSONObject message = new JSONObject();
message.put("collection", dbr.collection);
message.put("data", new JSONObject(dbr.data));
mSocket.emit("dbAdd", message, ack);
rxBus.send(new EventNSClientNewLog("DBADD " + dbr.collection, "Sent " + dbr.nsClientID));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void dbAdd(String collection, JSONObject data, Object originalObject) {
try {
if (!isConnected || !hasWriteAuth) return;
JSONObject message = new JSONObject();
message.put("collection", collection);
message.put("data", data);
mSocket.emit("dbAdd", message, new NSAddAck(aapsLogger, rxBus, originalObject));
rxBus.send(new EventNSClientNewLog("DBADD " + collection, "Sent " + originalObject.getClass().getSimpleName() + " " + data));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void sendAlarmAck(AlarmAck alarmAck) {
if (!isConnected || !hasWriteAuth) return;
mSocket.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime);
rxBus.send(new EventNSClientNewLog("ALARMACK ", alarmAck.level + " " + alarmAck.group + " " + alarmAck.silenceTime));
}
public void resend(final String reason) {
if (!isConnected || !hasWriteAuth) return;
handler.post(() -> {
if (mSocket == null || !mSocket.connected()) return;
rxBus.send(new EventNSClientNewLog("QUEUE", "Resend started: " + reason));
if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) {
aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + ((System.currentTimeMillis() - lastAckTime) / 1000L) + " sec");
return;
}
dataSyncSelector.processChangedBolusesCompat();
dataSyncSelector.processChangedCarbsCompat();
dataSyncSelector.processChangedBolusCalculatorResultsCompat();
dataSyncSelector.processChangedTemporaryBasalsCompat();
dataSyncSelector.processChangedExtendedBolusesCompat();
dataSyncSelector.processChangedProfileSwitchesCompat();
dataSyncSelector.processChangedGlucoseValuesCompat();
dataSyncSelector.processChangedTempTargetsCompat();
dataSyncSelector.processChangedFoodsCompat();
dataSyncSelector.processChangedTherapyEventsCompat();
dataSyncSelector.processChangedDeviceStatusesCompat();
if (uploadQueue.size() == 0)
return;
if (lastResendTime > System.currentTimeMillis() - 10 * 1000L) {
aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastResendTime: " + ((System.currentTimeMillis() - lastResendTime) / 1000L) + " sec");
return;
}
lastResendTime = System.currentTimeMillis();
CloseableIterator<DbRequest> iterator;
int maxcount = 30;
try {
iterator = databaseHelper.getDbRequestIterator();
try {
while (iterator.hasNext() && maxcount > 0) {
DbRequest dbr = iterator.next();
if (dbr.action.equals("dbAdd")) {
NSAddAck addAck = new NSAddAck(aapsLogger, rxBus, null);
dbAdd(dbr, addAck);
} else if (dbr.action.equals("dbRemove")) {
NSUpdateAck removeAck = new NSUpdateAck("dbRemove", dbr._id, aapsLogger, rxBus, null);
dbRemove(dbr, removeAck);
} else if (dbr.action.equals("dbUpdate")) {
NSUpdateAck updateAck = new NSUpdateAck("dbUpdate", dbr._id, aapsLogger, rxBus, null);
dbUpdate(dbr, updateAck);
}
maxcount--;
}
} finally {
iterator.close();
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
rxBus.send(new EventNSClientNewLog("QUEUE", "Resend ended: " + reason));
});
}
public void restart() {
destroy();
initialize();
}
private void handleAnnouncement(JSONObject announcement) {
boolean defaultVal = config.getNSCLIENT();
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
NSAlarm nsAlarm = new NSAlarm(announcement);
Notification notification = new NotificationWithAction(injector, nsAlarm);
rxBus.send(new EventNewNotification(notification));
rxBus.send(new EventNSClientNewLog("ANNOUNCEMENT", JsonHelper.safeGetString(announcement, "message", "received")));
aapsLogger.debug(LTag.NSCLIENT, announcement.toString());
}
}
private void handleAlarm(JSONObject alarm) {
boolean defaultVal = config.getNSCLIENT();
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
long snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L);
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
NSAlarm nsAlarm = new NSAlarm(alarm);
Notification notification = new NotificationWithAction(injector, nsAlarm);
rxBus.send(new EventNewNotification(notification));
}
rxBus.send(new EventNSClientNewLog("ALARM", JsonHelper.safeGetString(alarm, "message", "received")));
aapsLogger.debug(LTag.NSCLIENT, alarm.toString());
}
}
private void handleUrgentAlarm(JSONObject alarm) {
boolean defaultVal = config.getNSCLIENT();
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
long snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L);
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
NSAlarm nsAlarm = new NSAlarm(alarm);
Notification notification = new NotificationWithAction(injector, nsAlarm);
rxBus.send(new EventNewNotification(notification));
}
rxBus.send(new EventNSClientNewLog("URGENTALARM", JsonHelper.safeGetString(alarm, "message", "received")));
aapsLogger.debug(LTag.NSCLIENT, alarm.toString());
}
}
public List<JSONArray> splitArray(JSONArray array) {
List<JSONArray> ret = new ArrayList<>();
try {
int size = array.length();
int count = 0;
JSONArray newarr = null;
for (int i = 0; i < size; i++) {
if (count == 0) {
if (newarr != null) {
ret.add(newarr);
}
newarr = new JSONArray();
count = 20;
}
newarr.put(array.get(i));
--count;
}
if (newarr != null && newarr.length() > 0) {
ret.add(newarr);
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
ret = new ArrayList<>();
ret.add(array);
}
return ret;
}
}

View file

@ -0,0 +1,738 @@
package info.nightscout.androidaps.plugins.general.nsclient.services
import android.content.Intent
import android.os.*
import androidx.work.OneTimeWorkRequest
import com.google.common.base.Charsets
import com.google.common.hash.Hashing
import dagger.android.DaggerService
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventConfigBuilderChange
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.food.FoodPlugin.FoodWorker
import info.nightscout.androidaps.plugins.general.nsclient.*
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAuthAck
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck
import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin.NSClientSourceWorker
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JsonHelper.safeGetLong
import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.JsonHelper.safeGetStringAllowNull
import info.nightscout.androidaps.utils.T.Companion.days
import info.nightscout.androidaps.utils.T.Companion.mins
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.socket.client.IO
import io.socket.client.Socket
import io.socket.emitter.Emitter
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.net.URISyntaxException
import java.util.*
import javax.inject.Inject
class NSClientService : DaggerService() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var nsSettingsStatus: NSSettingsStatus
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var sp: SP
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var config: Config
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var repository: AppRepository
companion object {
private const val WATCHDOG_INTERVAL_MINUTES = 2
private const val WATCHDOG_RECONNECT_IN = 15
private const val WATCHDOG_MAX_CONNECTIONS = 5
}
private val disposable = CompositeDisposable()
// public PowerManager.WakeLock mWakeLock;
private val binder: IBinder = LocalBinder()
private var handler: Handler? = null
private var socket: Socket? = null
private var dataCounter = 0
private var connectCounter = 0
private var nsEnabled = false
private var nsAPISecret = ""
private var nsDevice = ""
private val nsHours = 48
private var lastAckTime: Long = 0
private var nsApiHashCode = ""
private val reconnections = ArrayList<Long>()
var isConnected = false
var hasWriteAuth = false
var nsURL = ""
var latestDateInReceivedData: Long = 0
override fun onCreate() {
super.onCreate()
// PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
// mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService");
// mWakeLock.acquire();
initialize()
disposable.add(rxBus
.toObservable(EventConfigBuilderChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
if (nsEnabled != nsClientPlugin.isEnabled(PluginType.GENERAL)) {
latestDateInReceivedData = 0
destroy()
initialize()
}
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventPreferenceChange ->
if (event.isChanged(resourceHelper, R.string.key_nsclientinternal_url) ||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_api_secret) ||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_paused)) {
latestDateInReceivedData = 0
destroy()
initialize()
}
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
aapsLogger.debug(LTag.NSCLIENT, "EventAppExit received")
destroy()
stopSelf()
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventNSClientRestart::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
latestDateInReceivedData = 0
restart()
}, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(NSAuthAck::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ack -> processAuthAck(ack) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(NSUpdateAck::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ack -> processUpdateAck(ack) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(NSAddAck::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ack -> processAddAck(ack) }, fabricPrivacy::logException)
)
}
override fun onDestroy() {
super.onDestroy()
disposable.clear()
// if (mWakeLock.isHeld()) mWakeLock.release();
}
private fun processAddAck(ack: NSAddAck) {
lastAckTime = dateUtil.now()
dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientAddAckWorker::class.java)
.setInputData(dataWorker.storeInputData(ack, null))
.build())
}
private fun processUpdateAck(ack: NSUpdateAck) {
lastAckTime = dateUtil.now()
dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientUpdateRemoveAckWorker::class.java)
.setInputData(dataWorker.storeInputData(ack, null))
.build())
}
fun processAuthAck(ack: NSAuthAck) {
var connectionStatus = "Authenticated ("
if (ack.read) connectionStatus += "R"
if (ack.write) connectionStatus += "W"
if (ack.write_treatment) connectionStatus += "T"
connectionStatus += ')'
isConnected = true
hasWriteAuth = ack.write && ack.write_treatment
rxBus.send(EventNSClientStatus(connectionStatus))
rxBus.send(EventNSClientNewLog("AUTH", connectionStatus))
if (!ack.write) {
rxBus.send(EventNSClientNewLog("ERROR", "Write permission not granted "))
}
if (!ack.write_treatment) {
rxBus.send(EventNSClientNewLog("ERROR", "Write treatment permission not granted "))
}
if (!hasWriteAuth) {
val noWritePerm = Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, resourceHelper.gs(R.string.nowritepermission), Notification.URGENT)
rxBus.send(EventNewNotification(noWritePerm))
} else {
rxBus.send(EventDismissNotification(Notification.NSCLIENT_NO_WRITE_PERMISSION))
}
}
inner class LocalBinder : Binder() {
val serviceInstance: NSClientService
get() = this@NSClientService
}
override fun onBind(intent: Intent): IBinder {
return binder
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
return START_STICKY
}
fun initialize() {
dataCounter = 0
readPreferences()
@Suppress("UnstableApiUsage", "DEPRECATION")
if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString()
rxBus.send(EventNSClientStatus("Initializing"))
if (!nsClientPlugin.isAllowed) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "not allowed"))
rxBus.send(EventNSClientStatus("Not allowed"))
} else if (nsClientPlugin.paused) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused"))
rxBus.send(EventNSClientStatus("Paused"))
} else if (!nsEnabled) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "disabled"))
rxBus.send(EventNSClientStatus("Disabled"))
} else if (nsURL != "" && (buildHelper.isEngineeringMode() || nsURL.toLowerCase(Locale.getDefault()).startsWith("https://"))) {
try {
rxBus.send(EventNSClientStatus("Connecting ..."))
val opt = IO.Options()
opt.forceNew = true
opt.reconnection = true
socket = IO.socket(nsURL, opt).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnect)
socket.on(Socket.EVENT_DISCONNECT, onDisconnect)
socket.on(Socket.EVENT_ERROR, onError)
socket.on(Socket.EVENT_CONNECT_ERROR, onError)
socket.on(Socket.EVENT_CONNECT_TIMEOUT, onError)
socket.on(Socket.EVENT_PING, onPing)
rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect"))
socket.connect()
socket.on("dataUpdate", onDataUpdate)
socket.on("announcement", onAnnouncement)
socket.on("alarm", onAlarm)
socket.on("urgent_alarm", onUrgentAlarm)
socket.on("clear_alarm", onClearAlarm)
}
} catch (e: URISyntaxException) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
rxBus.send(EventNSClientStatus("Wrong URL syntax"))
} catch (e: RuntimeException) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
rxBus.send(EventNSClientStatus("Wrong URL syntax"))
}
} else if (nsURL.toLowerCase(Locale.getDefault()).startsWith("http://")) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "NS URL not encrypted"))
rxBus.send(EventNSClientStatus("Not encrypted"))
} else {
rxBus.send(EventNSClientNewLog("NSCLIENT", "No NS URL specified"))
rxBus.send(EventNSClientStatus("Not configured"))
}
}
private val onConnect = Emitter.Listener {
connectCounter++
val socketId = socket?.id() ?: "NULL"
rxBus.send(EventNSClientNewLog("NSCLIENT", "connect #$connectCounter event. ID: $socketId"))
if (socket != null) sendAuthMessage(NSAuthAck(rxBus))
watchdog()
}
private fun watchdog() {
synchronized(reconnections) {
val now = dateUtil.now()
reconnections.add(now)
for (r in reconnections.reversed()) {
if (r < now - mins(WATCHDOG_INTERVAL_MINUTES.toLong()).msecs()) {
reconnections.remove(r)
}
}
rxBus.send(EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " minutes: " + reconnections.size + "/" + WATCHDOG_MAX_CONNECTIONS))
if (reconnections.size >= WATCHDOG_MAX_CONNECTIONS) {
val n = Notification(Notification.NS_MALFUNCTION, resourceHelper.gs(R.string.nsmalfunction), Notification.URGENT)
rxBus.send(EventNewNotification(n))
rxBus.send(EventNSClientNewLog("WATCHDOG", "pausing for $WATCHDOG_RECONNECT_IN minutes"))
nsClientPlugin.pause(true)
rxBus.send(EventNSClientUpdateGUI())
Thread {
SystemClock.sleep(mins(WATCHDOG_RECONNECT_IN.toLong()).msecs())
rxBus.send(EventNSClientNewLog("WATCHDOG", "re-enabling NSClient"))
nsClientPlugin.pause(false)
}.start()
}
}
}
private val onDisconnect = Emitter.Listener { args ->
aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", *args)
rxBus.send(EventNSClientNewLog("NSCLIENT", "disconnect event"))
}
@Synchronized fun destroy() {
socket?.off(Socket.EVENT_CONNECT)
socket?.off(Socket.EVENT_DISCONNECT)
socket?.off(Socket.EVENT_PING)
socket?.off("dataUpdate")
socket?.off("announcement")
socket?.off("alarm")
socket?.off("urgent_alarm")
socket?.off("clear_alarm")
rxBus.send(EventNSClientNewLog("NSCLIENT", "destroy"))
isConnected = false
hasWriteAuth = false
socket?.disconnect()
socket = null
}
private fun sendAuthMessage(ack: NSAuthAck?) {
val authMessage = JSONObject()
try {
authMessage.put("client", "Android_$nsDevice")
authMessage.put("history", nsHours)
authMessage.put("status", true) // receive status
authMessage.put("from", latestDateInReceivedData) // send data newer than
authMessage.put("secret", nsApiHashCode)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
return
}
rxBus.send(EventNSClientNewLog("AUTH", "requesting auth"))
socket?.emit("authorize", authMessage, ack)
}
fun readPreferences() {
nsEnabled = nsClientPlugin.isEnabled(PluginType.GENERAL)
nsURL = sp.getString(R.string.key_nsclientinternal_url, "")
nsAPISecret = sp.getString(R.string.key_nsclientinternal_api_secret, "")
nsDevice = sp.getString("careportal_enteredby", "")
}
private val onError = Emitter.Listener { args ->
var msg = "Unknown Error"
if (args.isNotEmpty() && args[0] != null) {
msg = args[0].toString()
}
rxBus.send(EventNSClientNewLog("ERROR", msg))
}
private val onPing = Emitter.Listener {
rxBus.send(EventNSClientNewLog("PING", "received"))
// send data if there is something waiting
resend("Ping received")
}
private val onAnnouncement = Emitter.Listener { args ->
/*
{
"level":0,
"title":"Announcement",
"message":"test",
"plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true},
"group":"Announcement",
"isAnnouncement":true,
"key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da"
}
*/
val data: JSONObject
try {
data = args[0] as JSONObject
handleAnnouncement(data)
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private val onAlarm = Emitter.Listener { args ->
/*
{
"level":1,
"title":"Warning HIGH",
"message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g",
"eventName":"high",
"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true},
"pushoverSound":"climb",
"debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}},
"group":"default",
"key":"simplealarms_1"
}
*/
val data: JSONObject
try {
data = args[0] as JSONObject
handleAlarm(data)
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private val onUrgentAlarm = Emitter.Listener { args: Array<Any> ->
val data: JSONObject
try {
data = args[0] as JSONObject
handleUrgentAlarm(data)
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private val onClearAlarm = Emitter.Listener { args ->
/*
{
"clear":true,
"title":"All Clear",
"message":"default - Urgent was ack'd",
"group":"default"
}
*/
val data: JSONObject
try {
data = args[0] as JSONObject
rxBus.send(EventNSClientNewLog("CLEARALARM", "received"))
rxBus.send(EventDismissNotification(Notification.NS_ALARM))
rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
aapsLogger.debug(LTag.NSCLIENT, data.toString())
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private val onDataUpdate = Emitter.Listener { args ->
handler?.post {
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"AndroidAPS:NSClientService_onDataUpdate")
wakeLock.acquire(3000)
try {
val data = args[0] as JSONObject
try {
// delta means only increment/changes are coming
val isDelta = data.has("delta")
rxBus.send(EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + if (isDelta) " delta" else " full"))
if (data.has("status")) {
val status = data.getJSONObject("status")
nsSettingsStatus.handleNewData(status)
} else if (!isDelta) {
rxBus.send(EventNSClientNewLog("ERROR", "Unsupported Nightscout version "))
}
if (data.has("profiles")) {
val profiles = data.getJSONArray("profiles")
if (profiles.length() > 0) {
// take the newest
val profileStoreJson = profiles[profiles.length() - 1] as JSONObject
rxBus.send(EventNSClientNewLog("PROFILE", "profile received"))
dataWorker.enqueue(
OneTimeWorkRequest.Builder(LocalProfilePlugin.NSProfileWorker::class.java)
.setInputData(dataWorker.storeInputData(profileStoreJson, null))
.build())
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
val bundle = Bundle()
bundle.putString("profile", profileStoreJson.toString())
bundle.putBoolean("delta", isDelta)
val intent = Intent(Intents.ACTION_NEW_PROFILE)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent)
}
}
}
if (data.has("treatments")) {
val treatments = data.getJSONArray("treatments")
val removedTreatments = JSONArray()
val addedOrUpdatedTreatments = JSONArray()
if (treatments.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments"))
for (index in 0 until treatments.length()) {
val jsonTreatment = treatments.getJSONObject(index)
val action = safeGetStringAllowNull(jsonTreatment, "action", null)
val mills = safeGetLong(jsonTreatment, "mills")
if (action == null) addedOrUpdatedTreatments.put(jsonTreatment) else if (action == "update") addedOrUpdatedTreatments.put(jsonTreatment) else if (action == "remove" && mills > dateUtil.now() - days(1).msecs()) // handle 1 day old deletions only
removedTreatments.put(jsonTreatment)
}
if (removedTreatments.length() > 0) {
dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientRemoveWorker::class.java)
.setInputData(dataWorker.storeInputData(removedTreatments, null))
.build())
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
val bundle = Bundle()
bundle.putString("treatments", removedTreatments.toString())
bundle.putBoolean("delta", isDelta)
val intent = Intent(Intents.ACTION_REMOVED_TREATMENT)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent)
}
}
if (addedOrUpdatedTreatments.length() > 0) {
dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientAddUpdateWorker::class.java)
.setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null))
.build())
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
val splitted = splitArray(addedOrUpdatedTreatments)
for (part in splitted) {
val bundle = Bundle()
bundle.putString("treatments", part.toString())
bundle.putBoolean("delta", isDelta)
val intent = Intent(Intents.ACTION_CHANGED_TREATMENT)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent)
}
}
}
}
if (data.has("devicestatus")) {
val devicestatuses = data.getJSONArray("devicestatus")
if (devicestatuses.length() > 0) {
rxBus.send(EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses"))
nsDeviceStatus.handleNewData(devicestatuses)
}
}
if (data.has("food")) {
val foods = data.getJSONArray("food")
if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods"))
dataWorker.enqueue(
OneTimeWorkRequest.Builder(FoodWorker::class.java)
.setInputData(dataWorker.storeInputData(foods, null))
.build())
}
if (data.has("mbgs")) {
val mbgArray = data.getJSONArray("mbgs")
if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs"))
dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientMbgWorker::class.java)
.setInputData(dataWorker.storeInputData(mbgArray, null))
.build())
}
if (data.has("cals")) {
val cals = data.getJSONArray("cals")
if (cals.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + cals.length() + " cals"))
// Calibrations ignored
}
if (data.has("sgvs")) {
val sgvs = data.getJSONArray("sgvs")
if (sgvs.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs"))
dataWorker.enqueue(OneTimeWorkRequest.Builder(NSClientSourceWorker::class.java)
.setInputData(dataWorker.storeInputData(sgvs, null))
.build())
val splitted = splitArray(sgvs)
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
for (part in splitted) {
val bundle = Bundle()
bundle.putString("sgvs", part.toString())
bundle.putBoolean("delta", isDelta)
val intent = Intent(Intents.ACTION_NEW_SGV)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
sendBroadcast(intent)
}
}
}
rxBus.send(EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)))
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
//rxBus.send(new EventNSClientNewLog("NSCLIENT", "onDataUpdate end");
} finally {
if (wakeLock.isHeld) wakeLock.release()
}
}
}
fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any) {
try {
if (_id == null) return
if (!isConnected || !hasWriteAuth) return
val message = JSONObject()
message.put("collection", collection)
message.put("_id", _id)
message.put("data", data)
socket?.emit("dbUpdate", message, NSUpdateAck("dbUpdate", _id, aapsLogger, rxBus, originalObject))
rxBus.send(EventNSClientNewLog("DBUPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " + _id))
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
}
fun dbRemove(collection: String, _id: String?, originalObject: Any) {
try {
if (_id == null) return
if (!isConnected || !hasWriteAuth) return
val message = JSONObject()
message.put("collection", collection)
message.put("_id", _id)
socket?.emit("dbRemove", message, NSUpdateAck("dbRemove", _id, aapsLogger, rxBus, originalObject))
rxBus.send(EventNSClientNewLog("DBREMOVE $collection", "Sent " + originalObject.javaClass.simpleName + " " + _id))
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
}
fun dbAdd(collection: String, data: JSONObject, originalObject: Any) {
try {
if (!isConnected || !hasWriteAuth) return
val message = JSONObject()
message.put("collection", collection)
message.put("data", data)
socket?.emit("dbAdd", message, NSAddAck(aapsLogger, rxBus, originalObject))
rxBus.send(EventNSClientNewLog("DBADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data))
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
}
fun sendAlarmAck(alarmAck: AlarmAck) {
if (!isConnected || !hasWriteAuth) return
socket?.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime)
rxBus.send(EventNSClientNewLog("ALARMACK ", alarmAck.level.toString() + " " + alarmAck.group + " " + alarmAck.silenceTime))
}
fun resend(reason: String) {
if (!isConnected || !hasWriteAuth) return
handler?.post {
if (socket?.connected() != true) return@post
if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) {
aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + (System.currentTimeMillis() - lastAckTime) / 1000L + " sec")
return@post
}
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"AndroidAPS:NSClientService_onDataUpdate")
wakeLock.acquire(mins(10).msecs())
try {
rxBus.send(EventNSClientNewLog("QUEUE", "Resend started: $reason"))
dataSyncSelector.doUpload()
rxBus.send(EventNSClientNewLog("QUEUE", "Resend ended: $reason"))
} finally {
if (wakeLock.isHeld) wakeLock.release()
}
}
}
fun restart() {
destroy()
initialize()
}
private fun handleAnnouncement(announcement: JSONObject) {
val defaultVal = config.NSCLIENT
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
val nsAlarm = NSAlarm(announcement)
val notification: Notification = NotificationWithAction(injector, nsAlarm)
rxBus.send(EventNewNotification(notification))
rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", safeGetString(announcement, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, announcement.toString())
}
}
private fun handleAlarm(alarm: JSONObject) {
val defaultVal = config.NSCLIENT
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
val snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
val nsAlarm = NSAlarm(alarm)
val notification: Notification = NotificationWithAction(injector, nsAlarm)
rxBus.send(EventNewNotification(notification))
}
rxBus.send(EventNSClientNewLog("ALARM", safeGetString(alarm, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
}
}
private fun handleUrgentAlarm(alarm: JSONObject) {
val defaultVal = config.NSCLIENT
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
val snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
val nsAlarm = NSAlarm(alarm)
val notification: Notification = NotificationWithAction(injector, nsAlarm)
rxBus.send(EventNewNotification(notification))
}
rxBus.send(EventNSClientNewLog("URGENTALARM", safeGetString(alarm, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
}
}
private fun splitArray(array: JSONArray): List<JSONArray> {
var ret: MutableList<JSONArray> = ArrayList()
try {
val size = array.length()
var count = 0
var newarr: JSONArray? = null
for (i in 0 until size) {
if (count == 0) {
if (newarr != null) ret.add(newarr)
newarr = JSONArray()
count = 20
}
newarr?.put(array[i])
--count
}
if (newarr != null && newarr.length() > 0) ret.add(newarr)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
ret = ArrayList()
ret.add(array)
}
return ret
}
init {
if (handler == null) {
val handlerThread = HandlerThread(NSClientService::class.java.simpleName + "Handler")
handlerThread.start()
handler = Handler(handlerThread.looper)
}
}
}

View file

@ -178,24 +178,24 @@ class OpenHumansUploader @Inject constructor(
}
}
@JvmOverloads
fun enqueueTreatment(treatment: Treatment?, deleted: Boolean = false) = treatment?.let {
insertQueueItem("Treatments") {
put("date", treatment.date)
put("isValid", treatment.isValid)
put("source", treatment.source)
put("nsId", treatment._id)
put("boluscalc", treatment.boluscalc)
put("carbs", treatment.carbs)
put("dia", treatment.dia)
put("insulin", treatment.insulin)
put("insulinInterfaceID", treatment.insulinInterfaceID)
put("isSMB", treatment.isSMB)
put("mealBolus", treatment.mealBolus)
put("bolusCalcJson", treatment.getBoluscalc())
put("isDeletion", deleted)
}
}
// @JvmOverloads
// fun enqueueTreatment(treatment: Treatment?, deleted: Boolean = false) = treatment?.let {
// insertQueueItem("Treatments") {
// put("date", treatment.date)
// put("isValid", treatment.isValid)
// put("source", treatment.source)
// put("nsId", treatment._id)
// put("boluscalc", treatment.boluscalc)
// put("carbs", treatment.carbs)
// put("dia", treatment.dia)
// put("insulin", treatment.insulin)
// put("insulinInterfaceID", treatment.insulinInterfaceID)
// put("isSMB", treatment.isSMB)
// put("mealBolus", treatment.mealBolus)
// put("bolusCalcJson", treatment.getBoluscalc())
// put("isDeletion", deleted)
// }
// }
@JvmOverloads
fun enqueueTherapyEvent(therapyEvent: TherapyEvent, deleted: Boolean = false) = insertQueueItem("TherapyEvents") {
@ -210,17 +210,17 @@ class OpenHumansUploader @Inject constructor(
put("isDeletion", deleted)
}
@JvmOverloads
fun enqueueExtendedBolus(extendedBolus: ExtendedBolus, deleted: Boolean = false) = insertQueueItem("ExtendedBoluses") {
put("date", extendedBolus.date)
put("isValid", extendedBolus.isValid)
put("source", extendedBolus.source)
put("nsId", extendedBolus._id)
put("pumpId", extendedBolus.pumpId)
put("insulin", extendedBolus.insulin)
put("durationInMinutes", extendedBolus.durationInMinutes)
put("isDeletion", deleted)
}
// @JvmOverloads
// fun enqueueExtendedBolus(extendedBolus: ExtendedBolus, deleted: Boolean = false) = insertQueueItem("ExtendedBoluses") {
// put("date", extendedBolus.date)
// put("isValid", extendedBolus.isValid)
// put("source", extendedBolus.source)
// put("nsId", extendedBolus._id)
// put("pumpId", extendedBolus.pumpId)
// put("insulin", extendedBolus.insulin)
// put("durationInMinutes", extendedBolus.durationInMinutes)
// put("isDeletion", deleted)
// }
// @JvmOverloads
// fun enqueueProfileSwitch(profileSwitch: ProfileSwitch, deleted: Boolean = false) = insertQueueItem("ProfileSwitches") {
@ -244,22 +244,22 @@ class OpenHumansUploader @Inject constructor(
// put("double", tdd.total)
// }
@JvmOverloads
fun enqueueTemporaryBasal(temporaryBasal: TemporaryBasal?, deleted: Boolean = false) = temporaryBasal?.let {
insertQueueItem("TemporaryBasals") {
put("date", temporaryBasal.date)
put("isValid", temporaryBasal.isValid)
put("source", temporaryBasal.source)
put("nsId", temporaryBasal._id)
put("pumpId", temporaryBasal.pumpId)
put("durationInMinutes", temporaryBasal.durationInMinutes)
put("durationInMinutes", temporaryBasal.durationInMinutes)
put("isAbsolute", temporaryBasal.isAbsolute)
put("percentRate", temporaryBasal.percentRate)
put("absoluteRate", temporaryBasal.absoluteRate)
put("isDeletion", deleted)
}
}
// @JvmOverloads
// fun enqueueTemporaryBasal(temporaryBasal: TemporaryBasal?, deleted: Boolean = false) = temporaryBasal?.let {
// insertQueueItem("TemporaryBasals") {
// put("date", temporaryBasal.date)
// put("isValid", temporaryBasal.isValid)
// put("source", temporaryBasal.source)
// put("nsId", temporaryBasal._id)
// put("pumpId", temporaryBasal.pumpId)
// put("durationInMinutes", temporaryBasal.durationInMinutes)
// put("durationInMinutes", temporaryBasal.durationInMinutes)
// put("isAbsolute", temporaryBasal.isAbsolute)
// put("percentRate", temporaryBasal.percentRate)
// put("absoluteRate", temporaryBasal.absoluteRate)
// put("isDeletion", deleted)
// }
// }
@JvmOverloads
fun enqueueTempTarget(tempTarget: TemporaryTarget?, deleted: Boolean = false) = tempTarget?.let {

View file

@ -10,6 +10,7 @@ import android.graphics.Paint
import android.graphics.drawable.AnimationDrawable
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
@ -24,10 +25,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.jjoe64.graphview.GraphView
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.TemporaryTarget
@ -124,7 +123,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
private lateinit var dm: DisplayMetrics
private var axisWidth: Int = 0
private var rangeToDisplay = 6 // for graph
private var loopHandler = Handler()
private lateinit var loopHandler: Handler
private var refreshLoop: Runnable? = null
private val secondaryGraphs = ArrayList<GraphView>()
@ -150,6 +149,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loopHandler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
// pre-process landscape mode
val screenWidth = dm.widthPixels
@ -342,6 +342,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
R.id.accept_temp_button -> {
profileFunction.getProfile() ?: return
if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopHandler.post {
val lastRun = loopPlugin.lastRun
loopPlugin.invoke("Accept temp button", false)
if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) {
@ -352,12 +353,13 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.buttonsLayout.acceptTempButton.visibility = View.GONE
(context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID)
rxBus.send(EventWearInitiateAction("cancelChangeRequest"))
loopPlugin.acceptChangeRequest()
Thread { loopPlugin.acceptChangeRequest() }.run()
})
})
}
}
}
}
R.id.aps_mode -> {
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable {
@ -860,7 +862,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
// **** BG ****
if (predictionsAvailable && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
graphData.addBgReadings(fromTime, toTime, highLine, apsResult?.predictions?.map { bg-> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }?.toMutableList())
graphData.addBgReadings(fromTime, toTime, highLine, apsResult?.predictions?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }?.toMutableList())
else graphData.addBgReadings(fromTime, toTime, highLine, null)
if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime)

View file

@ -400,9 +400,8 @@ class GraphData(
time += 5 * 60 * 1000L
continue
}
var iob = 0.0
var absIob = 0.0
iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile).iob
val iob: Double = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile).iob
if (absScale) absIob = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTemps(time).iob
if (abs(lastIob - iob) > 0.02) {
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
@ -461,8 +460,7 @@ class GraphData(
time += 5 * 60 * 1000L
continue
}
var iob = 0.0
iob = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTemps(time).iob
val iob: Double = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTemps(time).iob
if (abs(lastIob - iob) > 0.02) {
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
iobArray.add(ScaledDataPoint(time, iob, iobScale))

View file

@ -54,6 +54,9 @@ class LocalProfileFragment : DaggerFragment() {
private val save = Runnable {
doEdit()
basalView?.updateLabel(resourceHelper.gs(R.string.basal_label) + ": " + sumLabel())
localProfilePlugin.profile?.getSpecificProfile(spinner?.selectedItem.toString())?.let {
binding.basalGraph.show(ProfileSealed.Pure(it))
}
}
private val textWatch = object : TextWatcher {
@ -124,7 +127,7 @@ class LocalProfileFragment : DaggerFragment() {
binding.dia.setParams(currentProfile.dia, hardLimits.minDia(), hardLimits.maxDia(), 0.1, DecimalFormat("0.0"), false, binding.save, textWatch)
binding.dia.tag = "LP_DIA"
TimeListEdit(context, aapsLogger, dateUtil, view, R.id.ic, "IC", resourceHelper.gs(R.string.ic_label), currentProfile.ic, null, hardLimits.minIC(), hardLimits.maxIC(), 0.1, DecimalFormat("0.0"), save)
basalView = TimeListEdit(context, aapsLogger, dateUtil, view, R.id.basal, "BASAL", resourceHelper.gs(R.string.basal_label) + ": " + sumLabel(), currentProfile.basal, null, pumpDescription.basalMinimumRate, 10.0, 0.01, DecimalFormat("0.00"), save)
basalView = TimeListEdit(context, aapsLogger, dateUtil, view, R.id.basal_holder, "BASAL", resourceHelper.gs(R.string.basal_label) + ": " + sumLabel(), currentProfile.basal, null, pumpDescription.basalMinimumRate, 10.0, 0.01, DecimalFormat("0.00"), save)
if (units == Constants.MGDL) {
TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf, "ISF", resourceHelper.gs(R.string.isf_label), currentProfile.isf, null, HardLimits.MIN_ISF, HardLimits.MAX_ISF, 1.0, DecimalFormat("0"), save)
TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target, "TARGET", resourceHelper.gs(R.string.target_label), currentProfile.targetLow, currentProfile.targetHigh, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), 1.0, DecimalFormat("0"), save)
@ -162,6 +165,9 @@ class LocalProfileFragment : DaggerFragment() {
}
}
})
localProfilePlugin.profile?.getSpecificProfile(spinner?.selectedItem.toString())?.let {
binding.basalGraph.show(ProfileSealed.Pure(it))
}
binding.profileAdd.setOnClickListener {
if (localProfilePlugin.isEdited) {

View file

@ -1,6 +1,10 @@
package info.nightscout.androidaps.plugins.profile.local
import android.content.Context
import androidx.fragment.app.FragmentActivity
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
@ -12,7 +16,8 @@ import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HardLimits
@ -35,7 +40,6 @@ class LocalProfilePlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val sp: SP,
private val profileFunction: ProfileFunction,
private val nsUpload: NSUpload,
private val activePlugin: ActivePlugin,
private val hardLimits: HardLimits,
private val dateUtil: DateUtil
@ -144,7 +148,7 @@ class LocalProfilePlugin @Inject constructor(
if (name.contains(".")) namesOK = false
}
if (namesOK)
rawProfile?.let { nsUpload.uploadProfileStore(it.data) }
sp.putLong(R.string.key_local_profile_last_change, dateUtil.now())
else
activity?.let {
OKDialog.show(it, "", resourceHelper.gs(R.string.profilenamecontainsdot))
@ -167,61 +171,64 @@ class LocalProfilePlugin @Inject constructor(
p.dia = sp.getDouble(localProfileNumbered + "dia", Constants.defaultDIA)
try {
p.ic = JSONArray(sp.getString(localProfileNumbered + "ic", defaultArray))
} catch (e1: JSONException) {
try {
p.ic = JSONArray(defaultArray)
} catch (ignored: JSONException) {
}
aapsLogger.error("Exception", e1)
}
try {
p.isf = JSONArray(sp.getString(localProfileNumbered + "isf", defaultArray))
} catch (e1: JSONException) {
try {
p.isf = JSONArray(defaultArray)
} catch (ignored: JSONException) {
}
aapsLogger.error("Exception", e1)
}
try {
p.basal = JSONArray(sp.getString(localProfileNumbered + "basal", defaultArray))
} catch (e1: JSONException) {
try {
p.basal = JSONArray(defaultArray)
} catch (ignored: JSONException) {
}
aapsLogger.error("Exception", e1)
}
try {
p.targetLow = JSONArray(sp.getString(localProfileNumbered + "targetlow", defaultArray))
} catch (e1: JSONException) {
try {
p.targetLow = JSONArray(defaultArray)
} catch (ignored: JSONException) {
}
aapsLogger.error("Exception", e1)
}
try {
p.targetHigh = JSONArray(sp.getString(localProfileNumbered + "targethigh", defaultArray))
} catch (e1: JSONException) {
try {
p.targetHigh = JSONArray(defaultArray)
} catch (ignored: JSONException) {
}
aapsLogger.error("Exception", e1)
}
profiles.add(p)
} catch (e: JSONException) {
aapsLogger.error("Exception", e)
}
}
// create at least one profile if doesn't exist
if (profiles.size < 1) profiles.add(defaultProfile())
isEdited = false
numOfProfiles = profiles.size
createAndStoreConvertedProfile()
}
@Synchronized
fun loadFromStore(store: ProfileStore) {
try {
val newProfiles: ArrayList<SingleProfile> = ArrayList()
for (p in store.getProfileList()) {
store.getSpecificProfile(p.toString())?.let {
val sp = copyFrom(it, p.toString())
sp.name = p.toString()
newProfiles.add(sp)
}
}
if (newProfiles.size > 0) {
profiles = newProfiles
numOfProfiles = profiles.size
currentProfileIndex = 0
isEdited = false
createAndStoreConvertedProfile()
aapsLogger.debug(LTag.PROFILE, "Accepted ${profiles.size} profiles")
rxBus.send(EventLocalProfileChanged())
} else
aapsLogger.debug(LTag.PROFILE, "ProfileStore not accepted")
} catch (e: Exception) {
aapsLogger.error("Error loading ProfileStore", e)
}
}
private fun defaultProfile(): SingleProfile =
SingleProfile().also { p ->
p.name = Constants.LOCAL_PROFILE
p.mgdl = profileFunction.getUnits() == GlucoseUnit.MGDL
p.dia = Constants.defaultDIA
try {
p.ic = JSONArray(defaultArray)
p.isf = JSONArray(defaultArray)
p.basal = JSONArray(defaultArray)
p.targetLow = JSONArray(defaultArray)
p.targetHigh = JSONArray(defaultArray)
} catch (e: JSONException) {
aapsLogger.error("Exception", e)
}
}
fun copyFrom(pureProfile: PureProfile, newName: String): SingleProfile {
var verifiedName = newName
if (rawProfile?.getSpecificProfile(newName) != null) {
@ -380,4 +387,36 @@ class LocalProfilePlugin @Inject constructor(
get() = rawProfile?.getDefaultProfile()?.let {
DecimalFormatter.to2Decimal(ProfileSealed.Pure(it).percentageBasalSum()) + "U "
} ?: "INVALID"
// cannot be inner class because of needed injection
class NSProfileWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var sp: SP
@Inject lateinit var config: Config
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
override fun doWork(): Result {
val profileJson = dataWorker.pickupJSONObject(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
if (sp.getBoolean(R.string.key_ns_receive_profile_store, false) || config.NSCLIENT) {
localProfilePlugin.loadFromStore(ProfileStore(injector, profileJson, dateUtil))
aapsLogger.debug(LTag.PROFILE, "Received profileStore: $profileJson")
return Result.success(workDataOf("Data" to profileJson.toString()))
}
return Result.success()
}
}
}

View file

@ -1,170 +0,0 @@
package info.nightscout.androidaps.plugins.profile.ns
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.NsprofileFragmentBinding
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.profile.ns.events.EventNSProfileUpdateGUI
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
class NSProfileFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var nsProfilePlugin: NSProfilePlugin
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var config: Config
private var disposable: CompositeDisposable = CompositeDisposable()
private var _binding: NsprofileFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
_binding = NsprofileFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.profileviewer.closeLayout.close.visibility = View.GONE // not needed for fragment
binding.profileswitch.setOnClickListener {
val name = binding.spinner.selectedItem?.toString() ?: ""
nsProfilePlugin.profile?.let { store ->
store.getSpecificProfile(name)?.let {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.nsprofile),
resourceHelper.gs(R.string.activate_profile) + ": " + name + " ?", Runnable {
uel.log(Action.PROFILE_SWITCH, Sources.NSProfile,
ValueWithUnit.SimpleString(name),
ValueWithUnit.Percent(100))
profileFunction.createProfileSwitch(store, name, 0, 100, 0, dateUtil.now())
})
}
}
}
}
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
if (_binding == null) return
binding.profileviewer.invalidprofile.visibility = View.VISIBLE
binding.profileviewer.noprofile.visibility = View.VISIBLE
binding.profileviewer.units.text = ""
binding.profileviewer.dia.text = ""
binding.profileviewer.activeprofile.text = ""
binding.profileviewer.ic.text = ""
binding.profileviewer.isf.text = ""
binding.profileviewer.basal.text = ""
binding.profileviewer.basaltotal.text = ""
binding.profileviewer.target.text = ""
binding.profileswitch.visibility = View.GONE
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (_binding == null) return
val name = binding.spinner.getItemAtPosition(position).toString()
binding.profileswitch.visibility = View.GONE
nsProfilePlugin.profile?.let { store ->
store.getSpecificProfile(name)?.let { profile ->
if (_binding == null) return
val pss = ProfileSealed.Pure(profile)
binding.profileviewer.units.text = pss.units.asText
binding.profileviewer.dia.text = resourceHelper.gs(R.string.format_hours, pss.dia)
binding.profileviewer.activeprofile.text = name
binding.profileviewer.ic.text = pss.getIcList(resourceHelper, dateUtil)
binding.profileviewer.isf.text = pss.getIsfList(resourceHelper, dateUtil)
binding.profileviewer.basal.text = pss.getBasalList(resourceHelper, dateUtil)
binding.profileviewer.basaltotal.text = String.format(resourceHelper.gs(R.string.profile_total), DecimalFormatter.to2Decimal(pss.baseBasalSum()))
binding.profileviewer.target.text = pss.getTargetList(resourceHelper, dateUtil)
binding.profileviewer.basalGraph.show(pss)
if (pss.isValid("NSProfileFragment", activePlugin.activePump, config, resourceHelper, rxBus)) {
binding.profileviewer.invalidprofile.visibility = View.GONE
binding.profileswitch.visibility = View.VISIBLE
} else {
binding.profileviewer.invalidprofile.visibility = View.VISIBLE
binding.profileswitch.visibility = View.GONE
}
}
}
}
}
}
@Synchronized
override fun onResume() {
super.onResume()
disposable += rxBus
.toObservable(EventNSProfileUpdateGUI::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGUI() }, fabricPrivacy::logException)
updateGUI()
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
@Synchronized
fun updateGUI() {
if (_binding == null) return
binding.profileviewer.noprofile.visibility = View.VISIBLE
nsProfilePlugin.profile?.let { profileStore ->
context?.let { context ->
val profileList = profileStore.getProfileList()
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
binding.spinner.adapter = adapter
// set selected to actual profile
for (p in profileList.indices) {
if (profileList[p] == profileFunction.getProfileName())
binding.spinner.setSelection(p)
}
binding.profileviewer.noprofile.visibility = View.GONE
}
}
}
}

View file

@ -1,110 +0,0 @@
package info.nightscout.androidaps.plugins.profile.ns
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventProfileStoreChanged
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.ProfileSource
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.profile.ns.events.EventNSProfileUpdateGUI
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NSProfilePlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper,
resourceHelper: ResourceHelper,
private val sp: SP,
private val dateUtil: DateUtil,
config: Config
) : PluginBase(PluginDescription()
.mainType(PluginType.PROFILE)
.fragmentClass(NSProfileFragment::class.java.name)
.pluginIcon(R.drawable.ic_nightscout_profile)
.pluginName(R.string.nsprofile)
.shortName(R.string.profileviewer_shortname)
.alwaysEnabled(config.NSCLIENT)
.alwaysVisible(config.NSCLIENT)
.showInList(!config.NSCLIENT)
.description(R.string.description_profile_nightscout),
aapsLogger, resourceHelper, injector
), ProfileSource {
override var profile: ProfileStore? = null
override val profileName: String?
get() = profile?.getDefaultProfileName()
override fun onStart() {
super.onStart()
loadNSProfile()
}
private fun storeNSProfile() {
sp.putString("profile", profile!!.data.toString())
aapsLogger.debug(LTag.PROFILE, "Storing profile")
}
private fun loadNSProfile() {
aapsLogger.debug(LTag.PROFILE, "Loading stored profile")
val profileString = sp.getStringOrNull("profile", null)
if (profileString != null) {
aapsLogger.debug(LTag.PROFILE, "Loaded profile: $profileString")
profile = ProfileStore(injector, JSONObject(profileString), dateUtil)
} else {
aapsLogger.debug(LTag.PROFILE, "Stored profile not found")
// force restart of nsclient to fetch profile
rxBus.send(EventNSClientRestart())
}
}
// cannot be inner class because of needed injection
class NSProfileWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var nsProfilePlugin: NSProfilePlugin
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var dataWorker: DataWorker
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
override fun doWork(): Result {
val profileString = dataWorker.pickupJSONObject(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
nsProfilePlugin.profile = ProfileStore(injector, profileString, dateUtil)
nsProfilePlugin.storeNSProfile()
if (nsProfilePlugin.isEnabled()) {
rxBus.send(EventProfileStoreChanged())
rxBus.send(EventNSProfileUpdateGUI())
}
aapsLogger.debug(LTag.PROFILE, "Received profileStore: ${nsProfilePlugin.profile}")
return Result.success()
}
}
}

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.profile.ns.events
import info.nightscout.androidaps.events.EventUpdateGui
class EventNSProfileUpdateGUI : EventUpdateGui()

View file

@ -61,7 +61,7 @@ class MDIPlugin @Inject constructor(
override fun waitForDisconnectionInSeconds(): Int = 0
override fun stopConnecting() {}
override fun getPumpStatus(reason: String) {}
override fun setNewBasalProfile(profile: Profile): PumpEnactResult = PumpEnactResult(injector).success(true)
override fun setNewBasalProfile(profile: Profile): PumpEnactResult = PumpEnactResult(injector).success(true).enacted(true)
override fun isThisProfileSet(profile: Profile): Boolean = false
override fun lastDataTime(): Long = System.currentTimeMillis()
override val baseBasalRate: Double = 0.0

View file

@ -4,12 +4,12 @@ import android.os.SystemClock
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -25,8 +25,6 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.InstanceId.instanceId
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.TimeChangeType
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -139,7 +137,6 @@ open class VirtualPumpPlugin @Inject constructor(
override fun isHandshakeInProgress(): Boolean = false
override fun connect(reason: String) {
//if (!Config.NSCLIENT) NSUpload.uploadDeviceStatus()
lastDataTime = System.currentTimeMillis()
}
@ -152,12 +149,9 @@ open class VirtualPumpPlugin @Inject constructor(
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
lastDataTime = System.currentTimeMillis()
rxBus.send(EventNewNotification(Notification(Notification.PROFILE_SET_OK, resourceHelper.gs(R.string.profile_set_ok), Notification.INFO, 60)))
// Do nothing here. we are using database profile
val result = PumpEnactResult(injector)
result.success = true
val notification = Notification(Notification.PROFILE_SET_OK, resourceHelper.gs(R.string.profile_set_ok), Notification.INFO, 60)
rxBus.send(EventNewNotification(notification))
return result
return PumpEnactResult(injector).success(true).enacted(true)
}
override fun isThisProfileSet(profile: Profile): Boolean {
@ -400,7 +394,7 @@ open class VirtualPumpPlugin @Inject constructor(
aapsLogger.debug(LTag.PUMP, "Pump in configuration: $pumpType, PumpType object: $pumpTypeNew")
if (this.pumpType == pumpTypeNew) return
aapsLogger.debug(LTag.PUMP, "New pump configuration found ($pumpTypeNew), changing from previous (${this.pumpType})")
pumpDescription.setPumpDescription(pumpTypeNew)
pumpDescription.fillFor(pumpTypeNew)
this.pumpType = pumpTypeNew
}

View file

@ -5,12 +5,12 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.interfaces.BgSource
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
@ -115,7 +115,7 @@ class NSClientSourcePlugin @Inject constructor(
override fun doWork(): Result {
var ret = Result.success()
if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(R.string.key_ns_autobackfill, true) && !dexcomPlugin.isEnabled()) return Result.success()
if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(R.string.key_ns_receive_cgm, true) && !dexcomPlugin.isEnabled()) return Result.success()
val sgvs = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))

View file

@ -105,25 +105,10 @@ public class TreatmentService extends OrmLiteBaseService<DatabaseHelper> impleme
return wrapped.queryForAll();
}
public void delete(Treatment data) throws SQLException {
wrapped.delete(data);
openHumansUploader.enqueueTreatment(data, true);
}
public void create(Treatment data) throws SQLException {
wrapped.create(data);
openHumansUploader.enqueueTreatment(data);
}
public Treatment queryForId(long id) throws SQLException {
return wrapped.queryForId(id);
}
public void update(Treatment data) throws SQLException {
wrapped.update(data);
openHumansUploader.enqueueTreatment(data);
}
public QueryBuilder<Treatment, Long> queryBuilder() {
return wrapped.queryBuilder();
}

View file

@ -26,7 +26,6 @@ import info.nightscout.androidaps.interfaces.TreatmentServiceInterface;
import info.nightscout.androidaps.interfaces.TreatmentsInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
@ -37,12 +36,10 @@ import io.reactivex.disposables.CompositeDisposable;
@Singleton
public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface {
private final AapsSchedulers aapsSchedulers;
private final SP sp;
private final RxBusWrapper rxBus;
private final ProfileFunction profileFunction;
private final ActivePlugin activePlugin;
private final NSUpload nsUpload;
private final FabricPrivacy fabricPrivacy;
private final DateUtil dateUtil;
private final DatabaseHelperInterface databaseHelper;
@ -63,7 +60,6 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
SP sp,
ProfileFunction profileFunction,
ActivePlugin activePlugin,
NSUpload nsUpload,
FabricPrivacy fabricPrivacy,
DateUtil dateUtil,
DatabaseHelperInterface databaseHelper,
@ -81,13 +77,11 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
aapsLogger, resourceHelper, injector
);
this.rxBus = rxBus;
this.aapsSchedulers = aapsSchedulers;
this.sp = sp;
this.profileFunction = profileFunction;
this.activePlugin = activePlugin;
this.fabricPrivacy = fabricPrivacy;
this.dateUtil = dateUtil;
this.nsUpload = nsUpload;
this.databaseHelper = databaseHelper;
this.repository = repository;
}

View file

@ -156,7 +156,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
})
}
}
val nsUploadOnly = sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode()
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())

View file

@ -102,7 +102,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
}
}
val nsUploadOnly = sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode()
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())

View file

@ -21,12 +21,10 @@ import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.extensions.getCustomizedName
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.UploadQueueInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
@ -55,8 +53,6 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var uploadQueue: UploadQueueInterface
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var aapsSchedulers: AapsSchedulers
@ -103,7 +99,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
}
}
}
if (sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode()) binding.refreshFromNightscout.visibility = View.GONE
if (!sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()) binding.refreshFromNightscout.visibility = View.GONE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
}

View file

@ -23,7 +23,6 @@ import info.nightscout.androidaps.databinding.TreatmentsTemptargetFragmentBindin
import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.UploadQueueInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
@ -58,7 +57,6 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var uploadQueue: UploadQueueInterface
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var translator: Translator
@Inject lateinit var dateUtil: DateUtil
@ -99,7 +97,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
})
}
}
val nsUploadOnly = sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode()
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode()
if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.INVISIBLE
binding.showInvalidated.setOnCheckedChangeListener { _, _ ->
rxBus.send(EventTreatmentUpdateGui())
@ -151,7 +149,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
_binding = null
}
private inner class RecyclerViewAdapter internal constructor(private var tempTargetList: List<TemporaryTarget>) : RecyclerView.Adapter<TempTargetsViewHolder>() {
private inner class RecyclerViewAdapter(private var tempTargetList: List<TemporaryTarget>) : RecyclerView.Adapter<TempTargetsViewHolder>() {
private val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
private val currentlyActiveTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null

View file

@ -6,6 +6,7 @@ import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.logging.LTag
@ -25,9 +26,10 @@ class CommandSetProfile constructor(
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var config: Config
override fun execute() {
if (commandQueue.isThisProfileSet(profile)) {
if (commandQueue.isThisProfileSet(profile) && repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing) {
aapsLogger.debug(LTag.PUMPQUEUE, "Correct profile already set. profile: $profile")
callback?.result(PumpEnactResult(injector).success(true).enacted(false))?.run()
return
@ -37,7 +39,7 @@ class CommandSetProfile constructor(
callback?.result(r)?.run()
// Send SMS notification if ProfileSwitch is coming from NS
val profileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
if (profileSwitch is ValueWrapper.Existing && r.enacted && hasNsId) {
if (profileSwitch is ValueWrapper.Existing && r.enacted && hasNsId && !config.NSCLIENT) {
if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL))
smsCommunicatorPlugin.sendNotificationToAllNumbers(resourceHelper.gs(R.string.profile_set_ok))
}

View file

@ -129,7 +129,8 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() {
if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) {
localAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated, loopPlugin.isDisconnected)
}
if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE)) {
if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE)
|| profileFunction.getProfile() == null) {
rxBus.send(EventProfileSwitchChanged())
} else if (isStatusOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()

View file

@ -19,11 +19,8 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragm
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
@ -53,7 +50,6 @@ class SWDefinition @Inject constructor(
private val configBuilder: ConfigBuilder,
private val loopPlugin: LoopPlugin,
private val nsClientPlugin: NSClientPlugin,
private val nsProfilePlugin: NSProfilePlugin,
private val importExportPrefs: ImportExportPrefs,
private val androidPermission: AndroidPermission,
private val cryptoUtil: CryptoUtil,
@ -189,8 +185,8 @@ class SWDefinition @Inject constructor(
.label(R.string.status)
.initialStatus(nsClientPlugin.status)
)
.validator { nsClientPlugin.nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth }
.visibility { !(nsClientPlugin.nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth) }
.validator { nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true }
.visibility { !(nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true) }
private val screenPatientName = SWScreen(injector, R.string.patient_name)
.skippable(true)
.add(SWInfoText(injector)
@ -254,27 +250,14 @@ class SWDefinition @Inject constructor(
.option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description)
.label(R.string.configbuilder_bgsource))
.add(SWBreak(injector))
private val screenProfile = SWScreen(injector, R.string.configbuilder_profile)
.skippable(false)
.add(SWInfoText(injector)
.label(R.string.setupwizard_profile_description))
.add(SWBreak(injector))
.add(SWPlugin(injector, this)
.option(PluginType.PROFILE, R.string.configbuilder_profile_description)
.label(R.string.configbuilder_profile))
private val screenNsProfile = SWScreen(injector, R.string.nsprofile)
.skippable(false)
.add(SWInfoText(injector)
.label(R.string.adjustprofileinns))
.add(SWFragment(injector, this)
.add(NSProfileFragment()))
.validator { nsProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus) } ?: false }
.visibility { nsProfilePlugin.isEnabled() }
private val screenLocalProfile = SWScreen(injector, R.string.localprofile)
.skippable(false)
.add(SWFragment(injector, this)
.add(LocalProfileFragment()))
.validator { localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus) } ?: false }
.validator {
localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus) }
?: false
}
.visibility { localProfilePlugin.isEnabled() }
private val screenProfileSwitch = SWScreen(injector, R.string.careportal_profileswitch)
.skippable(false)
@ -399,8 +382,6 @@ class SWDefinition @Inject constructor(
.add(screenAge)
.add(screenInsulin)
.add(screenBgSource)
.add(screenProfile)
.add(screenNsProfile)
.add(screenLocalProfile)
.add(screenProfileSwitch)
.add(screenPump)
@ -428,8 +409,6 @@ class SWDefinition @Inject constructor(
.add(screenAge)
.add(screenInsulin)
.add(screenBgSource)
.add(screenProfile)
.add(screenNsProfile)
.add(screenLocalProfile)
.add(screenProfileSwitch)
.add(screenPump)

View file

@ -92,8 +92,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="5"
android:paddingBottom="10dp">
android:paddingBottom="10dp"
android:weightSum="5">
<TextView
android:id="@+id/dia_tab"
@ -228,11 +228,25 @@
<LinearLayout
android:id="@+id/basal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/basal_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
android:orientation="vertical" />
<info.nightscout.androidaps.plugins.treatments.fragments.ProfileGraph
android:id="@+id/basal_graph"
android:layout_width="match_parent"
android:layout_height="100dip"
android:layout_margin="20dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/target"
android:layout_width="match_parent"
@ -248,15 +262,15 @@
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="3dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="3dp"
android:layout_weight="1"
android:drawableStart="@drawable/ic_local_activate"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="@string/activate_profile"
android:drawableStart="@drawable/ic_local_activate" />
android:text="@string/activate_profile" />
<LinearLayout
android:layout_width="match_parent"

View file

@ -75,15 +75,6 @@
android:layout_height="wrap_content"
android:textStyle="normal|bold" />
<TextView
android:id="@+id/queue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:text="@string/queue"
android:textAlignment="viewEnd"
tools:ignore="RtlHardcoded" />
</LinearLayout>
<LinearLayout
@ -125,26 +116,6 @@
android:textColor="@android:color/holo_orange_light"
android:textStyle="normal|bold" />
<TextView
android:id="@+id/clear_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/clear_queue"
android:textAlignment="center"
android:textColor="@android:color/holo_orange_light"
android:textStyle="normal|bold" />
<TextView
android:id="@+id/show_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/show_queue"
android:textAlignment="center"
android:textColor="@android:color/holo_orange_light"
android:textStyle="normal|bold" />
<TextView
android:id="@+id/full_sync"
android:layout_width="wrap_content"

View file

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Spinner
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="5dp" />
<Button
android:id="@+id/profileswitch"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/activate_profile"
android:textColor="@color/colorProfileSwitchButton" />
<include
android:id="@+id/profileviewer"
layout="@layout/profileviewer_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View file

@ -1,453 +0,0 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".plugins.profile.ns.NSProfileFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/invalidprofile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/invalidprofile"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@android:color/holo_red_light"
android:textStyle="bold"
android:visibility="gone" />
<TextView
android:id="@+id/noprofile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/noprofileset"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@android:color/holo_red_light"
android:textStyle="bold"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/careportal_newnstreatment_profile_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/activeprofile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter"
android:visibility="gone" />
<LinearLayout
android:id="@+id/datelayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/date"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/units_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/units"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/dia_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/dia"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/ic_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/ic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/isf_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/isf"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/basal_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/basal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text=""
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=""
android:textSize="14sp" />
<TextView
android:id="@+id/basaltotal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="17dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<info.nightscout.androidaps.plugins.treatments.fragments.ProfileGraph
android:id="@+id/basal_graph"
android:layout_width="match_parent"
android:layout_height="100dip"
android:layout_margin="20dp" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="2"
android:gravity="end"
android:text="@string/target_label"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="0"
android:gravity="center_horizontal"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="start"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
<Button
android:id="@+id/reload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/reloadprofile"
android:visibility="gone" />
<include
android:id="@+id/close_layout"
layout="@layout/close" />
</LinearLayout>
</ScrollView>

View file

@ -47,6 +47,8 @@
<string name="key_ns_temporary_basal_last_synced_id" translatable="false">ns_temporary_basal_last_synced_id</string>
<string name="key_ns_extended_bolus_last_synced_id" translatable="false">ns_extended_bolus_last_synced_id</string>
<string name="key_ns_profile_switch_last_synced_id" translatable="false">profile_switch_last_synced_id</string>
<string name="key_ns_profile_store_last_synced_timestamp" translatable="false">ns_profile_store_last_synced_timestamp</string>
<string name="key_local_profile_last_change" translatable="false">local_profile_last_change</string>
<string name="treatmentssafety_title">Treatments safety</string>
<string name="treatmentssafety_maxbolus_title">Max allowed bolus [U]</string>
@ -76,7 +78,6 @@
<string name="description_overview">Displays the current state of your loop and buttons for most common actions</string>
<string name="description_persistent_notification">Shows an ongoing notification with a short overview of what your loop is doing</string>
<string name="description_profile_local">Define a profile which is available offline.</string>
<string name="description_profile_nightscout">Provides the profile you have defined in Nightscout</string>
<string name="description_pump_mdi">Pump integration for people who do multiple daily injections for their diabetes therapy</string>
<string name="description_pump_virtual">Pump integration for pumps which don\'t have any driver yet (Open Loop)</string>
<string name="description_sensitivity_aaps">Sensitivity is calculated the same way like Oref0, but you can specify timeframe to the past. Minimal carb absorption is calculated from max carb absorption time from preferences.</string>
@ -126,7 +127,6 @@
<string name="configbuilder">Config Builder</string>
<string name="overview">Overview</string>
<string name="nsprofile">NS Profile</string>
<string name="treatments">Treatments</string>
<string name="virtualpump">Virtual Pump</string>
<string name="careportal">Careportal</string>
@ -338,7 +338,6 @@
<string name="localprofile_shortname">LP</string>
<string name="overview_shortname">HOME</string>
<string name="virtualpump_shortname">VPUMP</string>
<string name="profileviewer_shortname">NSPROFILE</string>
<string name="treatments_shortname">TREAT</string>
<string name="objectives_shortname">OBJ</string>
<string name="wear_shortname">WEAR</string>
@ -497,10 +496,6 @@
<string name="xdripstatus_shortname">xds</string>
<string name="wear_showbgi_title">Show BGI</string>
<string name="wear_showbgi_summary">Add BGI to status line</string>
<string name="ns_noupload">No upload to NS</string>
<string name="ns_noupload_summary">All data sent to NS are dropped. AAPS is connected to NS but no change in NS is done</string>
<string name="key_ns_upload_only" translatable="false">ns_upload_only2</string>
<string name="key_ns_noupload" translatable="false">ns_noupload</string>
<string name="overview_extendedbolus_cancel_button">Cancel Extended Bolus</string>
<string name="doprofileswitch">Do Profile Switch</string>
<string name="careportal_sensor_label">Sensor</string>
@ -697,8 +692,6 @@
<string name="category">Category</string>
<string name="subcategory">Subcategory</string>
<string name="bolusrecordedonly">Bolus will be recorded only (not delivered by pump)</string>
<string name="ns_autobackfill_summary">Autobackfill missig BGs from NS</string>
<string name="key_ns_autobackfill" translatable="false">ns_autobackfill</string>
<string name="loop_smbsetbypump_label">SMB set by pump</string>
<string name="overview_show_activity">Activity</string>
<string name="overview_show_bgi">Blood Glucose Impact</string>
@ -781,7 +774,6 @@
<string name="virtualpump_type">Virtual Pump Type</string>
<string name="virtualpump_definition">Pump Definition</string>
<string name="virtualpump_pump_def">Bolus: Step=%1$s\nExtended Bolus: [Step=%2$s, Duration=%3$smin-%4$sh]\nBasal: Step=%5$s\nTBR: %6$s (by %7$s), Duration=%8$smin-%9$sh\n%10$s</string>
<string name="ns_autobackfill_title">Autobackfill BG</string>
<string name="wear_wizard_settings">Wizard Settings</string>
<string name="key_wearwizard_bg" translatable="false">wearwizard_bg</string>
<string name="key_wearwizard_tt" translatable="false">wearwizard_tt</string>
@ -795,7 +787,6 @@
<string name="enable_nsclient">Enable NSClient</string>
<string name="welcometosetupwizard">Welcome to setup wizard. It will guide you through the setup process\n</string>
<string name="readstatus">Read status</string>
<string name="adjustprofileinns">Changes must be done in NS</string>
<string name="exitwizard">Skip setup wizard</string>
<string name="setupwizard_loop_description">Press the button below to enable AndroidAPS to suggest/make basal changes</string>
<string name="key_setupwizard_processed" translatable="false">startupwizard_processed</string>
@ -803,7 +794,6 @@
<string name="setupwizard_sensitivity_url">https://github.com/MilosKozak/AndroidAPS/wiki/Sensitivity-detection-and-COB</string>
<string name="nsclientinfotext">NSClient handles connection to Nightscout. You can skip this part now but you will not be able to pass objectives until you set it up.</string>
<string name="diawarning">Please remember: new insulin profiles require DIA at least 5h. DIA 56h on new profile is equal to DIA 3h on old insulin profiles.</string>
<string name="setupwizard_profile_description">Please select source of profile. If patient is a child you should use NS profile. If there is nobody following you on Nightscout you will probably prefer Local profile. Please remember that you are only selecting the profile source. To use it you must activate it by executing \"Profile switch\"</string>
<string name="setupwizard_aps_description">Select one from availables algorithms. They are sorted from oldest to newest. Newer algorithm is usually more powerful and more aggressive. Thus if you are new looper you may probably start with AMA and not with latest one. Do not forget to read the OpenAPS documentation and configure it before use.</string>
<string name="setupwizard_pump_waiting_for_riley_link_connection">Please configure your RileyLink below. After selecting a RileyLink, it will be possible to continue setup once the RileyLink status is \"Connected\". This might take a minute.\n</string>
<string name="setupwizard_pump_pump_not_initialized"><b>Note:</b> You can continue setup once the pump has been set up.\n</string>
@ -952,7 +942,6 @@
<string name="objectives_button_unstart">Clear started</string>
<string name="doyouwantresetstart">Do you want reset objective start? You may lose your progress.</string>
<string name="setupwizard_units_prompt">Select units you want to display values in</string>
<string name="ns_ploadlocalprofile">Upload local profile changes to NS</string>
<string name="key_wear_detailediob" translatable="false">wear_detailediob</string>
<string name="key_wear_showbgi" translatable="false">wear_showbgi</string>
<string name="dia_short">DIA</string>
@ -1137,5 +1126,30 @@
<string name="profile_carbs_ratio_value">Profile carbs ratio value</string>
<string name="full_sync">Full sync</string>
<string name="prime">Prime</string>
<string name="ns_sync_options">Synchronization</string>
<string name="key_ns_upload" translatable="false">ns_upload</string>
<string name="ns_upload_summary">Profiles, boluses, carbs, temporary basals are uploaded to NS</string>
<string name="ns_upload">Upload data to NS</string>
<string name="key_ns_receive_profile_store" translatable="false">ns_receive_profile_store</string>
<string name="ns_receive_profile_store">Receive profile store</string>
<string name="ns_receive_profile_store_summary">Synchronized profiles from NS profile editor to Local profile</string>
<string name="key_ns_receive_temp_target" translatable="false">ns_receive_temp_target</string>
<string name="ns_receive_temp_target">Receive temporary targets</string>
<string name="ns_receive_temp_target_summary">Accept temporary targets entered through NS or NSClient</string>
<string name="key_ns_receive_profile_switch" translatable="false">ns_receive_profile_switch</string>
<string name="ns_receive_profile_switch">Receive profile switches</string>
<string name="ns_receive_profile_switch_summary">Accept profile switches entered through NS or NSClient</string>
<string name="key_ns_receive_insulin" translatable="false">ns_receive_insulin</string>
<string name="ns_receive_insulin">Receive insulin</string>
<string name="ns_receive_insulin_summary">Accept insulin entered through NS or NSClient (it\'s not delivered, only calculated towards IOB)</string>
<string name="key_ns_receive_carbs" translatable="false">ns_receive_carbs</string>
<string name="ns_receive_carbs">Receive carbs</string>
<string name="ns_receive_carbs_summary">Accept carbs entered through NS or NSClient</string>
<string name="key_ns_receive_therapy_events" translatable="false">ns_receive_therapy_events</string>
<string name="ns_receive_therapy_events">Receive therapy events</string>
<string name="ns_receive_therapy_events_summary">Accept therapy events (cannula, insulin, battery change etc) entered through NS or NSClient</string>
<string name="key_ns_receive_cgm" translatable="false">ns_receive_cgm</string>
<string name="ns_receive_cgm">Receive/backfill CGM data</string>
<string name="ns_receive_cgm_summary">Accept CGM data from NS</string>
</resources>

View file

@ -26,17 +26,59 @@
validate:minLength="12"
validate:testType="minLength"/>
<androidx.preference.PreferenceScreen
android:key="@string/ns_sync_options"
android:title="@string/ns_sync_options">
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_logappstartedevent"
android:title="@string/ns_logappstartedevent"
android:summary="@string/ns_logappstartedevent"
/>
android:key="@string/key_ns_upload"
android:summary="@string/ns_upload_summary"
android:title="@string/ns_upload" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_uploadlocalprofile"
android:title="@string/ns_ploadlocalprofile" />
android:key="@string/key_ns_receive_cgm"
android:summary="@string/ns_receive_cgm_summary"
android:title="@string/ns_receive_cgm" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_receive_profile_store"
android:summary="@string/ns_receive_profile_store_summary"
android:title="@string/ns_receive_profile_store" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_receive_temp_target"
android:summary="@string/ns_receive_temp_target_summary"
android:title="@string/ns_receive_temp_target" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_receive_profile_switch"
android:summary="@string/ns_receive_profile_switch_summary"
android:title="@string/ns_receive_profile_switch" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_receive_insulin"
android:summary="@string/ns_receive_insulin_summary"
android:title="@string/ns_receive_insulin" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_receive_carbs"
android:summary="@string/ns_receive_carbs_summary"
android:title="@string/ns_receive_carbs" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_receive_therapy_events"
android:summary="@string/ns_receive_therapy_events_summary"
android:title="@string/ns_receive_therapy_events" />
</androidx.preference.PreferenceScreen>>
<androidx.preference.PreferenceScreen
android:key="@string/ns_alarmoptions"
@ -113,9 +155,10 @@
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_autobackfill"
android:summary="@string/ns_autobackfill_summary"
android:title="@string/ns_autobackfill_title" />
android:key="@string/key_ns_logappstartedevent"
android:title="@string/ns_logappstartedevent"
android:summary="@string/ns_logappstartedevent"
/>
<SwitchPreference
android:defaultValue="true"
@ -135,18 +178,6 @@
android:summary="@string/ns_localbroadcasts"
android:title="@string/ns_localbroadcasts_title" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_upload_only"
android:summary="@string/ns_upload_only_summary"
android:title="@string/ns_upload_only" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_noupload"
android:summary="@string/ns_noupload_summary"
android:title="@string/ns_noupload" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_sync_use_absolute"

View file

@ -18,7 +18,6 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugi
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage
@ -64,8 +63,6 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
@Mock lateinit var glimpPlugin: GlimpPlugin
@Mock lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin
@Mock lateinit var profiler: Profiler
@Mock lateinit var nsUpload: NSUpload
@Mock lateinit var uploadQueue: UploadQueueInterface
@Mock lateinit var uel: UserEntryLogger
@Mock lateinit var loggerUtils: LoggerUtils
@Mock lateinit var databaseHelper: DatabaseHelperInterface
@ -144,7 +141,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
comboPlugin = ComboPlugin(injector, aapsLogger, rxBus, resourceHelper, profileFunction, sp, commandQueue, context, databaseHelper, pumpSync, dateUtil)
danaRPlugin = DanaRPlugin(injector, aapsLogger, aapsSchedulers, rxBus, context, resourceHelper, constraintChecker, activePlugin, sp, commandQueue, danaPump, dateUtil, fabricPrivacy, pumpSync)
danaRSPlugin = DanaRSPlugin(injector, aapsLogger, aapsSchedulers, rxBus, context, resourceHelper, constraintChecker, profileFunction, sp, commandQueue, danaPump, pumpSync, detailedBolusInfoStorage, temporaryBasalStorage, fabricPrivacy, dateUtil)
insightPlugin = LocalInsightPlugin(injector, aapsLogger, rxBus, resourceHelper, treatmentsInterface, sp, commandQueue, profileFunction, nsUpload, context, uploadQueue, ConfigImpl(), dateUtil, databaseHelper, pumpSync)
insightPlugin = LocalInsightPlugin(injector, aapsLogger, rxBus, resourceHelper, treatmentsInterface, sp, commandQueue, profileFunction, context, ConfigImpl(), dateUtil, databaseHelper, pumpSync)
openAPSSMBPlugin = OpenAPSSMBPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, iobCobCalculator, hardLimits, profiler, sp, dateUtil, repository, glucoseStatusProvider)
openAPSAMAPlugin = OpenAPSAMAPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, iobCobCalculator, hardLimits, profiler, fabricPrivacy, dateUtil, repository, glucoseStatusProvider)
safetyPlugin = SafetyPlugin(injector, aapsLogger, resourceHelper, sp, rxBus, constraintChecker, openAPSAMAPlugin, openAPSSMBPlugin, sensitivityOref1Plugin, activePlugin, hardLimits, BuildHelper(ConfigImpl(), loggerUtils), iobCobCalculator, ConfigImpl(), dateUtil)

View file

@ -6,8 +6,6 @@ import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.db.TemporaryBasal
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.UploadQueueInterface
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -24,8 +22,6 @@ class TreatmentsPluginTest : TestBaseWithProfile() {
@Mock lateinit var sp: SP
@Mock lateinit var treatmentService: TreatmentService
@Mock lateinit var nsUpload: NSUpload
@Mock lateinit var uploadQueue: UploadQueueInterface
@Mock lateinit var repository: AppRepository
@Mock lateinit var databaseHelper: DatabaseHelperInterface

View file

@ -8,7 +8,6 @@ import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.elements.InputString
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.TimerUtil
@ -24,7 +23,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
@RunWith(PowerMockRunner::class)
@PrepareForTest(NSUpload::class, RxBusWrapper::class, TimerUtil::class)
@PrepareForTest(RxBusWrapper::class, TimerUtil::class)
class ActionAlarmTest : TestBase() {
@Mock lateinit var resourceHelper: ResourceHelper

View file

@ -6,11 +6,8 @@ import info.nightscout.androidaps.TestBase
import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.androidaps.database.transactions.Transaction
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.elements.InputString
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.junit.Assert
@ -21,12 +18,11 @@ import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
@RunWith(PowerMockRunner::class)
@PrepareForTest(NSUpload::class, RxBusWrapper::class)
@PrepareForTest(RxBusWrapper::class)
class ActionNotificationTest : TestBase() {
@Mock lateinit var resourceHelper: ResourceHelper

View file

@ -175,7 +175,7 @@ public class ComboPlugin extends PumpPluginBase implements Pump, Constraints {
this.pumpSync = pumpSync;
this.dateUtil = dateUtil;
pumpDescription.setPumpDescription(PumpType.ACCU_CHEK_COMBO);
pumpDescription.fillFor(PumpType.ACCU_CHEK_COMBO);
}
@Override protected void onStart() {

View file

@ -425,7 +425,7 @@ class TDDStatsActivity : NoSplashAppCompatActivity() {
private fun isOldData(historyList: List<TotalDailyDose>): Boolean {
val type = activePlugin.activePump.pumpDescription.pumpType
val startsYesterday = type == PumpType.DANA_R || type == PumpType.DANA_RS || type == PumpType.DANA_RV2 || type == PumpType.DANA_R_KOREAN || type == PumpType.ACCU_CHEK_INSIGHT
val startsYesterday = type == PumpType.DANA_R || type == PumpType.DANA_RS || type == PumpType.DANA_RV2 || type == PumpType.DANA_R_KOREAN || type == PumpType.ACCU_CHEK_INSIGHT_VIRTUAL
val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
return historyList.size < 3 || df.format(Date(historyList[0].timestamp)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0))
}

View file

@ -1,728 +0,0 @@
package info.nightscout.androidaps.data;
import static info.nightscout.androidaps.extensions.ProfileSwitchExtensionKt.pureProfileFromJson;
import androidx.annotation.NonNull;
import androidx.collection.LongSparseArray;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.DecimalFormat;
import java.util.TimeZone;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.interfaces.ActivePlugin;
import info.nightscout.androidaps.interfaces.Config;
import info.nightscout.androidaps.interfaces.GlucoseUnit;
import info.nightscout.androidaps.interfaces.Profile;
import info.nightscout.androidaps.interfaces.Pump;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
public class ProfileImplOld implements Profile {
@Inject public AAPSLogger aapsLogger;
@Inject public ActivePlugin activePlugin;
@Inject public ResourceHelper resourceHelper;
@Inject public RxBusWrapper rxBus;
@Inject public FabricPrivacy fabricPrivacy;
@Inject public Config config;
@Inject public DateUtil dateUtil;
private final HasAndroidInjector injector;
private JSONObject json;
private String jsonUnits;
private double dia; // TODO change to insulinInterface link
private TimeZone timeZone;
private JSONArray isf;
private LongSparseArray<Double> isf_v; // oldest at index 0
private JSONArray ic;
private LongSparseArray<Double> ic_v; // oldest at index 0
private JSONArray basal;
private LongSparseArray<Double> basal_v; // oldest at index 0
private JSONArray targetLow;
private LongSparseArray<Double> targetLow_v; // oldest at index 0
private JSONArray targetHigh;
private LongSparseArray<Double> targetHigh_v; // oldest at index 0
private int percentage;
private int timeshift;
protected boolean isValid;
protected boolean isValidated;
protected ProfileImplOld(HasAndroidInjector injector) {
injector.androidInjector().inject(this);
this.injector = injector;
}
@NonNull @Override
public String toString() {
if (json != null)
return json.toString();
else
return "Profile has no JSON";
}
// Constructor from profileStore JSON
public ProfileImplOld(HasAndroidInjector injector, JSONObject json, GlucoseUnit units) {
this(injector);
init(json, 100, 0);
if (this.jsonUnits == null) {
if (units != null)
this.jsonUnits = units.getAsText();
else {
fabricPrivacy.logCustom("Profile failover failed too");
this.jsonUnits = Constants.MGDL;
}
}
}
public ProfileImplOld(HasAndroidInjector injector, JSONObject json, int percentage, int timeshift) {
this(injector);
init(json, percentage, timeshift);
}
protected void init(JSONObject json, int percentage, int timeshift) {
if (json == null) return;
jsonUnits = null;
dia = Constants.defaultDIA;
timeZone = TimeZone.getDefault();
isf_v = null;
ic_v = null;
basal_v = null;
targetLow_v = null;
targetHigh_v = null;
isValid = true;
isValidated = false;
this.percentage = percentage;
this.timeshift = timeshift;
this.json = json;
try {
if (json.has("units"))
jsonUnits = json.getString("units").toLowerCase();
if (json.has("dia"))
dia = json.getDouble("dia");
if (json.has("timezone"))
timeZone = TimeZone.getTimeZone(json.getString("timezone"));
isf = json.getJSONArray("sens");
ic = json.getJSONArray("carbratio");
basal = json.getJSONArray("basal");
targetLow = json.getJSONArray("target_low");
targetHigh = json.getJSONArray("target_high");
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
isValid = false;
isValidated = true;
}
}
public String log() {
String ret = "\n";
for (int hour = 0; hour < 24; hour++) {
double value = getBasalTimeFromMidnight(hour * 60 * 60);
ret += "NS basal value for " + hour + ":00 is " + value + "\n";
}
ret += "NS units: " + getUnits();
return ret;
}
public JSONObject getData() {
if (!json.has("units"))
try {
json.put("units", jsonUnits);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
return json;
}
public double getDia() {
return dia;
}
// mmol or mg/dl
public void setUnits(String units) {
this.jsonUnits = units;
}
public GlucoseUnit getUnits() {
if (jsonUnits.equals(Constants.MMOL)) return GlucoseUnit.MMOL;
else return GlucoseUnit.MGDL;
}
TimeZone getTimeZone() {
return timeZone;
}
private LongSparseArray<Double> convertToSparseArray(JSONArray array) {
if (array == null) {
isValid = false;
return new LongSparseArray<>();
}
double multiplier = getMultiplier(array);
LongSparseArray<Double> sparse = new LongSparseArray<>();
for (int index = 0; index < array.length(); index++) {
try {
final JSONObject o = array.getJSONObject(index);
long tas;
try {
String time = o.getString("time");
tas = getShitfTimeSecs(dateUtil.toSeconds(time));
} catch (JSONException e) {
//log.debug(">>>>>>>>>>>> Used recalculated timeAsSeconds: " + time + " " + tas);
tas = getShitfTimeSecs((int) o.getLong("timeAsSeconds"));
}
double value = o.getDouble("value") * multiplier;
sparse.put(tas, value);
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
aapsLogger.error(json.toString());
fabricPrivacy.logException(e);
}
}
// check if start is at 0 (midnight)
// and add last value before midnight if not
if (sparse.keyAt(0) != 0) {
sparse.put(0, sparse.valueAt(sparse.size() - 1));
}
return sparse;
}
public synchronized boolean isValid(String from, Pump pump, Config config, ResourceHelper resourceHelper, RxBusWrapper rxBus) {
if (!isValid)
return false;
if (!isValidated) {
if (basal_v == null)
basal_v = convertToSparseArray(basal);
validate(basal_v);
if (isf_v == null)
isf_v = convertToSparseArray(isf);
validate(isf_v);
if (ic_v == null)
ic_v = convertToSparseArray(ic);
validate(ic_v);
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
validate(targetLow_v);
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
validate(targetHigh_v);
if (targetHigh_v.size() != targetLow_v.size()) isValid = false;
else for (int i = 0; i < targetHigh_v.size(); i++)
if (targetHigh_v.valueAt(i) < targetLow_v.valueAt(i))
isValid = false;
isValidated = true;
}
boolean notify = true;
if (isValid) {
// Check for hours alignment
if (!pump.getPumpDescription().is30minBasalRatesCapable()) {
for (int index = 0; index < basal_v.size(); index++) {
long secondsFromMidnight = basal_v.keyAt(index);
if (notify && secondsFromMidnight % 3600 != 0) {
if (config.getAPS()) {
Notification notification = new Notification(Notification.BASAL_PROFILE_NOT_ALIGNED_TO_HOURS, resourceHelper.gs(R.string.basalprofilenotaligned, from), Notification.NORMAL);
rxBus.send(new EventNewNotification(notification));
}
}
}
}
// Check for minimal basal value
PumpDescription description = pump.getPumpDescription();
for (int i = 0; i < basal_v.size(); i++) {
if (basal_v.valueAt(i) < description.getBasalMinimumRate()) {
basal_v.setValueAt(i, description.getBasalMinimumRate());
if (notify)
sendBelowMinimumNotification(from);
} else if (basal_v.valueAt(i) > description.getBasalMaximumRate()) {
basal_v.setValueAt(i, description.getBasalMaximumRate());
if (notify)
sendAboveMaximumNotification(from);
}
}
}
return isValid;
}
protected void sendBelowMinimumNotification(String from) {
rxBus.send(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, resourceHelper.gs(R.string.minimalbasalvaluereplaced, from), Notification.NORMAL)));
}
protected void sendAboveMaximumNotification(String from) {
rxBus.send(new EventNewNotification(new Notification(Notification.MAXIMUM_BASAL_VALUE_REPLACED, resourceHelper.gs(R.string.maximumbasalvaluereplaced, from), Notification.NORMAL)));
}
private void validate(LongSparseArray array) {
if (array.size() == 0) {
isValid = false;
return;
}
for (int index = 0; index < array.size(); index++) {
if (array.valueAt(index).equals(0d)) {
isValid = false;
return;
}
}
}
/*
private Double getValueToTime(JSONArray array, Integer timeAsSeconds) {
Double lastValue = null;
for (Integer index = 0; index < array.length(); index++) {
try {
JSONObject o = array.getJSONObject(index);
Integer tas = o.getInt("timeAsSeconds");
Double value = o.getDouble("value");
if (lastValue == null) lastValue = value;
if (timeAsSeconds < tas) {
break;
}
lastValue = value;
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return lastValue;
}
*/
Integer getShitfTimeSecs(Integer originalTime) {
int shiftedTime = originalTime + timeshift * 60 * 60;
shiftedTime = (shiftedTime + 24 * 60 * 60) % (24 * 60 * 60);
return shiftedTime;
}
private double getMultiplier(LongSparseArray<Double> array) {
double multiplier = 1d;
if (array == isf_v)
multiplier = 100d / percentage;
else if (array == ic_v)
multiplier = 100d / percentage;
else if (array == basal_v)
multiplier = percentage / 100d;
else
aapsLogger.error("Unknown array type");
return multiplier;
}
private double getMultiplier(JSONArray array) {
double multiplier = 1d;
if (array == isf)
multiplier = 100d / percentage;
else if (array == ic)
multiplier = 100d / percentage;
else if (array == basal)
multiplier = percentage / 100d;
else if (array == targetLow)
multiplier = 1d;
else if (array == targetHigh)
multiplier = 1d;
else
aapsLogger.error("Unknown array type");
return multiplier;
}
private double getValueToTime(LongSparseArray<Double> array, Integer timeAsSeconds) {
Double lastValue = null;
for (int index = 0; index < array.size(); index++) {
long tas = array.keyAt(index);
double value = array.valueAt(index);
if (lastValue == null) lastValue = value;
if (timeAsSeconds < tas) {
break;
}
lastValue = value;
}
return lastValue;
}
private String getValuesList(LongSparseArray<Double> array, LongSparseArray<Double> array2, DecimalFormat format, String units) {
String retValue = "";
for (Integer index = 0; index < array.size(); index++) {
retValue += dateUtil.format_HH_MM((int) array.keyAt(index));
retValue += " ";
retValue += format.format(array.valueAt(index));
if (array2 != null) {
retValue += " - ";
retValue += format.format(array2.valueAt(index));
}
retValue += " " + units;
if (index + 1 < array.size())
retValue += "\n";
}
return retValue;
}
public double getIsfMgdl() {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getIsfTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight()), getUnits());
}
public double getIsfMgdl(long time) {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getIsfTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight(time)), getUnits());
}
public double getIsfMgdlTimeFromMidnight(int timeAsSeconds) {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getIsfTimeFromMidnight(timeAsSeconds), getUnits());
}
public double getIsfTimeFromMidnight(int timeAsSeconds) {
if (isf_v == null)
isf_v = convertToSparseArray(isf);
return getValueToTime(isf_v, timeAsSeconds);
}
public String getIsfList(ResourceHelper resourceHelper, DateUtil dateUtil) {
if (isf_v == null)
isf_v = convertToSparseArray(isf);
return getValuesList(isf_v, null, new DecimalFormat("0.0"), getUnits().getAsText() + resourceHelper.gs(R.string.profile_per_unit));
}
public ProfileValue[] getIsfsMgdlValues() {
if (isf_v == null)
isf_v = convertToSparseArray(ic);
ProfileValue[] ret = new ProfileValue[isf_v.size()];
for (int index = 0; index < isf_v.size(); index++) {
int tas = (int) isf_v.keyAt(index);
double value = isf_v.valueAt(index);
ret[index] = new ProfileValue(tas, info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(value, getUnits()));
}
return ret;
}
public double getIc() {
return getIcTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight());
}
public double getIc(long time) {
return getIcTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight(time));
}
public double getIcTimeFromMidnight(int timeAsSeconds) {
if (ic_v == null)
ic_v = convertToSparseArray(ic);
return getValueToTime(ic_v, timeAsSeconds);
}
public String getIcList(ResourceHelper resourceHelper, DateUtil dateUtil) {
if (ic_v == null)
ic_v = convertToSparseArray(ic);
return getValuesList(ic_v, null, new DecimalFormat("0.0"), resourceHelper.gs(R.string.profile_carbs_per_unit));
}
public ProfileValue[] getIcsValues() {
if (ic_v == null)
ic_v = convertToSparseArray(ic);
ProfileValue[] ret = new ProfileValue[ic_v.size()];
for (int index = 0; index < ic_v.size(); index++) {
int tas = (int) ic_v.keyAt(index);
double value = ic_v.valueAt(index);
ret[index] = new ProfileValue(tas, value);
}
return ret;
}
public double getBasal() {
return getBasalTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight());
}
public double getBasal(long time) {
return getBasalTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight(time));
}
public synchronized double getBasalTimeFromMidnight(int timeAsSeconds) {
if (basal_v == null) {
basal_v = convertToSparseArray(basal);
}
return getValueToTime(basal_v, timeAsSeconds);
}
public String getBasalList(ResourceHelper resourceHelper, DateUtil dateUtil) {
if (basal_v == null)
basal_v = convertToSparseArray(basal);
return getValuesList(basal_v, null, new DecimalFormat("0.00"), resourceHelper.gs(R.string.profile_ins_units_per_hour));
}
@NonNull @Override public JSONObject toPureNsJson(DateUtil dateUtil) {
return getData();
}
public synchronized ProfileValue[] getBasalValues() {
if (basal_v == null)
basal_v = convertToSparseArray(basal);
ProfileValue[] ret = new ProfileValue[basal_v.size()];
for (int index = 0; index < basal_v.size(); index++) {
int tas = (int) basal_v.keyAt(index);
double value = basal_v.valueAt(index);
ret[index] = new ProfileValue(tas, value);
}
return ret;
}
public double getTargetMgdl() {
return getTargetMgdl(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight());
}
public double getTargetMgdl(int timeAsSeconds) {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl((getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds)) / 2, getUnits());
}
public double getTargetLowMgdl() {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getTargetLowTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight()), getUnits());
}
public double getTargetLowMgdl(long time) {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getTargetLowTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight(time)), getUnits());
}
double getTargetLowTimeFromMidnight(int timeAsSeconds) {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
return getValueToTime(targetLow_v, timeAsSeconds);
}
public double getTargetLowMgdlTimeFromMidnight(int timeAsSeconds) {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getTargetLowTimeFromMidnight(timeAsSeconds), getUnits());
}
public double getTargetHighMgdl() {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getTargetHighTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight()), getUnits());
}
public double getTargetHighMgdl(long time) {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getTargetHighTimeFromMidnight(info.nightscout.androidaps.interfaces.Profile.Companion.secondsFromMidnight(time)), getUnits());
}
public double getTargetHighTimeFromMidnight(int timeAsSeconds) {
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
return getValueToTime(targetHigh_v, timeAsSeconds);
}
public double getTargetHighMgdlTimeFromMidnight(int timeAsSeconds) {
return info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(getTargetHighTimeFromMidnight(timeAsSeconds), getUnits());
}
public static class TargetValue {
TargetValue(int timeAsSeconds, double low, double high) {
this.timeAsSeconds = timeAsSeconds;
this.low = low;
this.high = high;
}
public int timeAsSeconds;
public double low;
public double high;
}
public TargetValue[] getTargets() {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
TargetValue[] ret = new TargetValue[targetLow_v.size()];
for (int index = 0; index < targetLow_v.size(); index++) {
int tas = (int) targetLow_v.keyAt(index);
double low = targetLow_v.valueAt(index);
double high = targetHigh_v.valueAt(index);
ret[index] = new TargetValue(tas, low, high);
}
return ret;
}
public ProfileValue[] getSingleTargetsMgdl() {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
ProfileValue[] ret = new ProfileValue[targetLow_v.size()];
for (int index = 0; index < targetLow_v.size(); index++) {
int tas = (int) targetLow_v.keyAt(index);
double target = (targetLow_v.valueAt(index) + targetHigh_v.valueAt(index)) / 2;
ret[index] = new ProfileValue(tas, info.nightscout.androidaps.interfaces.Profile.Companion.toMgdl(target, getUnits()));
}
return ret;
}
@NonNull public String getTargetList(ResourceHelper resourceHelper, DateUtil dateUtil) {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
return getValuesList(targetLow_v, targetHigh_v, new DecimalFormat("0.0"), getUnits().getAsText());
}
public double getMaxDailyBasal() {
double max = 0d;
for (int hour = 0; hour < 24; hour++) {
double value = getBasalTimeFromMidnight(hour * 60 * 60);
if (value > max) max = value;
}
return max;
}
public double percentageBasalSum() {
double result = 0d;
for (int i = 0; i < 24; i++) {
result += getBasalTimeFromMidnight(i * 60 * 60);
}
return result;
}
public double baseBasalSum() {
double result = 0d;
for (int i = 0; i < 24; i++) {
result += getBasalTimeFromMidnight(i * 60 * 60) / getMultiplier(basal_v);
}
return result;
}
public int getPercentage() {
return percentage;
}
public int getTimeshift() {
return timeshift;
}
public PureProfile convertToNonCustomizedProfile(DateUtil dateUtil) {
JSONObject o = new JSONObject();
try {
o.put("units", jsonUnits);
o.put("dia", dia);
o.put("timezone", timeZone.getID());
// SENS
JSONArray sens = new JSONArray();
double lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getIsfTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
sens.put(item);
}
}
o.put("sens", sens);
// CARBRATIO
JSONArray carbratio = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getIcTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
carbratio.put(item);
}
}
o.put("carbratio", carbratio);
// BASAL
JSONArray basal = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getBasalTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
basal.put(item);
}
}
o.put("basal", basal);
// TARGET_LOW
JSONArray target_low = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getTargetLowTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
target_low.put(item);
}
}
o.put("target_low", target_low);
// TARGET_HIGH
JSONArray target_high = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getTargetHighTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
target_high.put(item);
}
}
o.put("target_high", target_high);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception" + e);
}
return pureProfileFromJson(o, dateUtil);
}
}

View file

@ -1,77 +0,0 @@
package info.nightscout.androidaps.db;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
@DatabaseTable(tableName = "DanaRHistory")
public class DanaRHistoryRecord {
@DatabaseField
public String _id;
@DatabaseField
public byte recordCode;
@DatabaseField(id = true)
public String bytes;
@DatabaseField
public long recordDate;
@DatabaseField
public double recordValue;
@DatabaseField
public String bolusType;
@DatabaseField
public String stringRecordValue;
@DatabaseField
public int recordDuration;
@DatabaseField
public double recordDailyBasal;
@DatabaseField
public double recordDailyBolus;
@DatabaseField
public String recordAlarm;
public DanaRHistoryRecord() {
this.recordDate = 0;
this.recordValue = 0.0;
this.bolusType = "None";
this.recordCode = 0x0F;
this.bytes = new String();
this._id = null;
}
public int getRecordLevel(double dExLow, double dLow, double dHigh, double dExHigh) {
if (this.recordValue < dExLow)
return 0;
if (this.recordValue < dLow)
return 1;
if (this.recordValue < dHigh)
return 2;
return this.recordValue < dExHigh ? 3 : 4;
}
public void setBytes(byte[] raw) {
this.bytes = bytesToHex(raw);
}
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}

View file

@ -1,86 +0,0 @@
package info.nightscout.androidaps.db;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Created by mike on 27.02.2016.
* <p>
* Allowed actions "dbAdd" || "dbUpdate" || "dbUpdateUnset" || "dbRemove"
*/
@DatabaseTable(tableName = "DBRequests")
public class DbRequest {
@DatabaseField(id = true)
public String nsClientID = null;
@DatabaseField
public String action = null;
@DatabaseField
public String collection = null;
@DatabaseField
public String data = null;
@DatabaseField
public String _id = null;
public DbRequest() {
}
// dbAdd
public DbRequest(String action, String collection, JSONObject json, long nsClientId) {
this.action = action;
this.collection = collection;
this.nsClientID = "" + nsClientId;
try {
json.put("NSCLIENT_ID", nsClientID);
} catch (JSONException e) {
e.printStackTrace();
}
this.data = json.toString();
this._id = "";
}
// dbUpdate, dbUpdateUnset
public DbRequest(String action, String collection, String _id, JSONObject json, long nsClientId) {
this.action = action;
this.collection = collection;
this.nsClientID = "" + nsClientId;
try {
json.put("NSCLIENT_ID", nsClientID);
} catch (JSONException e) {
e.printStackTrace();
}
this.data = json.toString();
this._id = _id;
}
// dbRemove
public DbRequest(String action, String collection, String _id, long nsClientId) {
JSONObject json = new JSONObject();
this.action = action;
this.collection = collection;
this.nsClientID = "" + nsClientId;
try {
json.put("NSCLIENT_ID", nsClientID);
} catch (JSONException e) {
e.printStackTrace();
}
this.data = json.toString();
this._id = _id;
}
public String log() {
return
"\nnsClientID:" + nsClientID +
"\naction:" + action +
"\ncollection:" + collection +
"\ndata:" + data +
"\n_id:" + _id;
}
}

View file

@ -2,29 +2,18 @@ package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.data.ProfileImplOld
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.db.ExtendedBolus
import info.nightscout.androidaps.db.TemporaryBasal
import info.nightscout.androidaps.db.Treatment
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.plugins.aps.loop.APSResult
import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
@Module
@Suppress("unused")
abstract class CoreDataClassesModule {
@ContributesAndroidInjector abstract fun nsMbgInjector(): NSMbg
@ContributesAndroidInjector abstract fun pumpEnactResultInjector(): PumpEnactResult
@ContributesAndroidInjector abstract fun apsResultInjector(): APSResult
@ContributesAndroidInjector abstract fun autosensDataInjector(): AutosensData
@ContributesAndroidInjector abstract fun profileInjector(): ProfileImplOld
@ContributesAndroidInjector abstract fun profileStoreInjector(): ProfileStore
@ContributesAndroidInjector abstract fun treatmentInjector(): Treatment
@ContributesAndroidInjector abstract fun temporaryBasalInjector(): TemporaryBasal
@ContributesAndroidInjector abstract fun extendedBolusInjector(): ExtendedBolus
}

View file

@ -4,14 +4,9 @@ import android.content.Context
import android.preference.PreferenceManager
import dagger.Module
import dagger.Provides
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.AAPSLoggerProduction
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.resources.ResourceHelperImplementation
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -25,12 +20,6 @@ import javax.inject.Singleton
])
open class CoreModule {
@Provides
@Singleton
fun provideProfileFunction(aapsLogger: AAPSLogger, sp: SP, resourceHelper: ResourceHelper, activePlugin: ActivePlugin, repository: AppRepository, dateUtil: DateUtil): ProfileFunction {
return ProfileFunctionImplementation(aapsLogger, sp, resourceHelper, activePlugin, repository, dateUtil)
}
@Provides
@Singleton
fun provideResources(context: Context): ResourceHelper = ResourceHelperImplementation(context)

View file

@ -5,7 +5,6 @@ import info.nightscout.androidaps.database.data.TargetBlock
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
private fun getShiftedTimeSecs(originalSeconds: Int, timeShiftHours: Int): Int {
@ -86,33 +85,18 @@ fun blockFromJsonArray(jsonArray: JSONArray?, dateUtil: DateUtil): List<Block>?
val ret = ArrayList<Block>(size)
try {
for (index in 0 until jsonArray.length() - 1) {
val o: JSONObject = jsonArray.getJSONObject(index)
val tas: Int = try {
o.getInt("timeAsSeconds")
} catch (e: JSONException) {
val time = o.getString("time")
dateUtil.toSeconds(time)
}
val next: JSONObject = jsonArray.getJSONObject(index + 1)
val nextTas: Int = try {
next.getInt("timeAsSeconds")
} catch (e: JSONException) {
val time = next.getString("time")
dateUtil.toSeconds(time)
}
val value: Double = o.getDouble("value")
val o = jsonArray.getJSONObject(index)
val tas = dateUtil.toSeconds(o.getString("time"))
val next = jsonArray.getJSONObject(index + 1)
val nextTas = dateUtil.toSeconds(next.getString("time"))
val value = o.getDouble("value")
if (tas % 3600 != 0) return null
if (nextTas % 3600 != 0) return null
ret.add(index, Block((nextTas - tas) * 1000L, value))
}
val last: JSONObject = jsonArray.getJSONObject(jsonArray.length() - 1)
val lastTas: Int = try {
last.getInt("timeAsSeconds")
} catch (e: JSONException) {
val time = last.getString("time")
dateUtil.toSeconds(time)
}
val value: Double = last.getDouble("value")
val lastTas = dateUtil.toSeconds(last.getString("time"))
val value = last.getDouble("value")
ret.add(jsonArray.length() - 1, Block((T.hours(24).secs() - lastTas) * 1000L, value))
} catch (e: Exception) {
return null
@ -128,41 +112,23 @@ fun targetBlockFromJsonArray(jsonArray1: JSONArray?, jsonArray2: JSONArray?, dat
try {
for (index in 0 until jsonArray1.length() - 1) {
val o1: JSONObject = jsonArray1.getJSONObject(index)
val tas1: Int = try {
o1.getInt("timeAsSeconds")
} catch (e: JSONException) {
val time = o1.getString("time")
dateUtil.toSeconds(time)
}
val value1: Double = o1.getDouble("value")
val next1: JSONObject = jsonArray1.getJSONObject(index + 1)
val nextTas1: Int = try {
next1.getInt("timeAsSeconds")
} catch (e: JSONException) {
val time = next1.getString("time")
dateUtil.toSeconds(time)
}
val o2: JSONObject = jsonArray2.getJSONObject(index)
val tas2: Int = try {
o2.getInt("timeAsSeconds")
} catch (e: JSONException) {
val time = o2.getString("time")
dateUtil.toSeconds(time)
}
val value2: Double = o2.getDouble("value")
val tas1 = dateUtil.toSeconds(o1.getString("time"))
val value1 = o1.getDouble("value")
val next1 = jsonArray1.getJSONObject(index + 1)
val nextTas1 = dateUtil.toSeconds(next1.getString("time"))
val o2 = jsonArray2.getJSONObject(index)
val tas2 = dateUtil.toSeconds(o2.getString("time"))
val value2 = o2.getDouble("value")
if (tas1 != tas2) return null
if (tas1 % 3600 != 0) return null
if (nextTas1 % 3600 != 0) return null
ret.add(index, TargetBlock((nextTas1 - tas1) * 1000L, value1, value2))
}
val last1: JSONObject = jsonArray1.getJSONObject(jsonArray1.length() - 1)
val lastTas1: Int = try {
last1.getInt("timeAsSeconds")
} catch (e: JSONException) {
val time = last1.getString("time")
dateUtil.toSeconds(time)
}
val value1: Double = last1.getDouble("value")
val last2: JSONObject = jsonArray2.getJSONObject(jsonArray2.length() - 1)
val value2: Double = last2.getDouble("value")
val last1 = jsonArray1.getJSONObject(jsonArray1.length() - 1)
val lastTas1 = dateUtil.toSeconds(last1.getString("time"))
val value1 = last1.getDouble("value")
val last2 = jsonArray2.getJSONObject(jsonArray2.length() - 1)
val value2 = last2.getDouble("value")
ret.add(jsonArray1.length() - 1, TargetBlock((T.hours(24).secs() - lastTas1) * 1000L, value1, value2))
} catch (e: Exception) {
return null

View file

@ -85,6 +85,7 @@ fun extendedBolusFromNsIdForInvalidating(nsId: String): ExtendedBolus =
JSONObject()
.put("mills", 1)
.put("amount", -1.0)
.put("enteredinsulin", -1.0)
.put("duration", -1.0)
.put("splitNow", 0)
.put("splitExt", 100)

View file

@ -7,6 +7,7 @@ import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.ProfileSwitch
import info.nightscout.androidaps.database.entities.TemporaryBasal
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.utils.DateUtil
@ -23,7 +24,7 @@ fun ProfileSwitch.toJson(dateUtil: DateUtil): JSONObject =
.put("eventType", TherapyEvent.Type.PROFILE_SWITCH.text)
.put("duration", T.msecs(duration).mins())
.put("profile", getCustomizedName())
.put("profileJson", ProfileSealed.PS(this).toPureNsJson(dateUtil))
.put("profileJson", ProfileSealed.PS(this).toPureNsJson(dateUtil).toString())
.put("timeshift", T.msecs(timeshift).hours())
.put("percentage", percentage)
.also {
@ -33,7 +34,20 @@ fun ProfileSwitch.toJson(dateUtil: DateUtil): JSONObject =
if (interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId)
}
fun profileSwitchFromJson(jsonObject: JSONObject, dateUtil: DateUtil): ProfileSwitch? {
/* NS PS
{
"_id":"608ffa268db0676196a772d7",
"enteredBy":"undefined",
"eventType":"Profile Switch",
"duration":10,
"profile":"LocalProfile0",
"created_at":"2021-05-03T13:26:58.537Z",
"utcOffset":0,
"mills":1620048418537,
"mgdl":98
}
*/
fun profileSwitchFromJson(jsonObject: JSONObject, dateUtil: DateUtil, activePlugin: ActivePlugin): ProfileSwitch? {
val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null
val duration = JsonHelper.safeGetLong(jsonObject, "duration")
val timeshift = JsonHelper.safeGetLong(jsonObject, "timeshift")
@ -41,15 +55,21 @@ fun profileSwitchFromJson(jsonObject: JSONObject, dateUtil: DateUtil): ProfileSw
val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true)
val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null)
val profileName = JsonHelper.safeGetStringAllowNull(jsonObject, "profile", null) ?: return null
val profileJson = JsonHelper.safeGetStringAllowNull(jsonObject, "profileJson", null) ?: return null
val profileJson = JsonHelper.safeGetStringAllowNull(jsonObject, "profileJson", null)
val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null)
val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null))
val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null)
if (timestamp == 0L) return null
val pureProfile = pureProfileFromJson(JSONObject(profileJson), dateUtil) ?: return null
val pureProfile =
if (profileJson == null) { // entered through NS, no JSON attached
val profilePlugin = activePlugin.activeProfileSource
val store = profilePlugin.profile ?: return null
store.getSpecificProfile(profileName) ?: return null
} else pureProfileFromJson(JSONObject(profileJson), dateUtil) ?: return null
val profileSealed = ProfileSealed.Pure(pureProfile)
return ProfileSwitch(
timestamp = timestamp,
basalBlocks = profileSealed.basalBlocks,
@ -74,9 +94,9 @@ fun profileSwitchFromJson(jsonObject: JSONObject, dateUtil: DateUtil): ProfileSw
/**
* Pure profile doesn't contain timestamp, percentage, timeshift, profileName
*/
fun pureProfileFromJson(jsonObject: JSONObject, dateUtil: DateUtil): PureProfile? {
fun pureProfileFromJson(jsonObject: JSONObject, dateUtil: DateUtil, defaultUnits: String? = null): PureProfile? {
try {
JsonHelper.safeGetStringAllowNull(jsonObject, "units", null) ?: return null
JsonHelper.safeGetStringAllowNull(jsonObject, "units", defaultUnits) ?: return null
val units = GlucoseUnit.fromText(JsonHelper.safeGetString(jsonObject, "units", Constants.MGDL))
val dia = JsonHelper.safeGetDoubleAllowNull(jsonObject, "dia") ?: return null
val timezone = TimeZone.getTimeZone(JsonHelper.safeGetString(jsonObject, "timezone", "UTC"))

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.interfaces
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.entities.*
import org.json.JSONObject
interface DataSyncSelector {
@ -15,6 +16,9 @@ interface DataSyncSelector {
data class PairTemporaryBasal(val value: TemporaryBasal, val updateRecordId: Long)
data class PairExtendedBolus(val value: ExtendedBolus, val updateRecordId: Long)
data class PairProfileSwitch(val value: ProfileSwitch, val updateRecordId: Long)
data class PairProfileStore(val value: JSONObject, val timestampSync: Long)
fun doUpload()
fun resetToNextFullSync()
@ -72,4 +76,7 @@ interface DataSyncSelector {
fun changedProfileSwitch() : List<ProfileSwitch>
// Until NS v3
fun processChangedProfileSwitchesCompat(): Boolean
fun confirmLastProfileStore(lastSynced: Long)
fun processChangedProfileStore()
}

View file

@ -1,28 +1,17 @@
package info.nightscout.androidaps.interfaces
import com.j256.ormlite.dao.CloseableIterator
import info.nightscout.androidaps.db.*
import org.json.JSONObject
interface DatabaseHelperInterface {
fun resetDatabases()
fun createOrUpdate(record: DanaRHistoryRecord)
fun createOrUpdate(record: OmnipodHistoryRecord)
fun createOrUpdate(record: InsightBolusID)
fun createOrUpdate(record: InsightPumpID)
fun createOrUpdate(record: InsightHistoryOffset)
fun createOrUpdate(record: OHQueueItem)
fun create(record: DbRequest)
fun getDanaRHistoryRecordsByType(type: Byte): List<DanaRHistoryRecord>
fun size(table: String): Long
fun deleteAllDbRequests()
fun deleteDbRequest(id: String): Int
fun delete(extendedBolus: ExtendedBolus)
fun deleteDbRequestbyMongoId(action: String, _id: String)
fun getDbRequestIterator(): CloseableIterator<DbRequest>
fun roundDateToSec(date: Long): Long
fun createOrUpdate(tempBasal: TemporaryBasal): Boolean
@Deprecated("Use new DB")
fun findTempBasalByPumpId(id: Long): TemporaryBasal?
@ -41,7 +30,6 @@ interface DatabaseHelperInterface {
fun getOHQueueSize(): Long
fun clearOpenHumansQueue()
fun getCountOfAllRows(): Long
fun removeAllOHQueueItemsWithIdSmallerThan(id: Long)
companion object {

View file

@ -20,7 +20,7 @@ interface IobCobCalculator {
fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData?
fun calculateAbsInsulinFromTreatmentsAndTemps(fromTime: Long): IobTotal
fun calculateFromTreatmentsAndTemps(time: Long, profile: Profile): IobTotal
fun calculateFromTreatmentsAndTemps(fromTime: Long, profile: Profile): IobTotal
fun getBasalData(profile: Profile, fromTime: Long): BasalData

View file

@ -2,7 +2,6 @@ package info.nightscout.androidaps.interfaces
import androidx.collection.ArrayMap
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.ProfileImplOld
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.logging.AAPSLogger
@ -53,12 +52,13 @@ class ProfileStore(val injector: HasAndroidInjector, val data: JSONObject, val d
fun getSpecificProfile(profileName: String): PureProfile? {
var profile: PureProfile? = null
val defaultUnits = JsonHelper.safeGetStringAllowNull(data, "units", null)
getStore()?.let { store ->
if (store.has(profileName)) {
profile = cachedObjects[profileName]
if (profile == null) {
JsonHelper.safeGetJSONObject(store, profileName, null)?.let { profileObject ->
profile = pureProfileFromJson(profileObject, dateUtil)
profile = pureProfileFromJson(profileObject, dateUtil, defaultUnits)
cachedObjects[profileName] = profile
}
}
@ -67,8 +67,7 @@ class ProfileStore(val injector: HasAndroidInjector, val data: JSONObject, val d
return profile
}
fun getSpecificProfileJson(profileName: String): JSONObject? {
var profile: PureProfile? = null
private fun getSpecificProfileJson(profileName: String): JSONObject? {
getStore()?.let { store ->
if (store.has(profileName))
return JsonHelper.safeGetJSONObject(store, profileName, null)

View file

@ -7,7 +7,7 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
class PumpDescription() {
constructor(pumpType: PumpType) : this() {
setPumpDescription(pumpType)
fillFor(pumpType)
}
var pumpType = PumpType.GENERIC_AAPS
@ -69,7 +69,7 @@ class PumpDescription() {
hasCustomUnreachableAlertCheck = false
}
fun setPumpDescription(pumpType: PumpType) {
fun fillFor(pumpType: PumpType) {
resetSettings()
this.pumpType = pumpType
val pumpCapability = pumpType.pumpCapability ?: return

View file

@ -344,6 +344,19 @@ interface PumpSync {
**/
fun invalidateTemporaryBasal(id: Long): Boolean
/**
* Invalidate of temporary basals that failed to start
* MDT specific
*
* If exists, isValid is set false
* If db record doesn't exist data is ignored and false returned
*
*
* @param temporaryId temporary id of temporary basal
* @return true if running record is found and invalidated
**/
fun invalidateTemporaryBasalWithTempId(temporaryId: Long): Boolean
/**
* Synchronization of extended bolus
*

View file

@ -1,6 +0,0 @@
package info.nightscout.androidaps.interfaces
interface UploadQueueAdminInterface : UploadQueueInterface {
fun clearQueue()
}

View file

@ -1,16 +0,0 @@
package info.nightscout.androidaps.interfaces;
import org.json.JSONObject;
import info.nightscout.androidaps.db.DbRequest;
public interface UploadQueueInterface {
long size();
void add(DbRequest dbRequest);
void removeByMongoId(final String action, final String _id);
String textList();
}

View file

@ -48,7 +48,6 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) {
var usePercent = false
var duration = 0
var tempBasalRequested = false
var bolusRequested = false
var iob: IobTotal? = null
var json: JSONObject? = JSONObject()
var hasPredictions = false
@ -161,7 +160,6 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) {
newResult.rate = rate
newResult.duration = duration
newResult.tempBasalRequested = tempBasalRequested
newResult.bolusRequested = bolusRequested
newResult.iob = iob
newResult.json = JSONObject(json.toString())
newResult.hasPredictions = hasPredictions
@ -309,11 +307,11 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) {
// closed loop mode: handle change at driver level
if (closedLoopEnabled.value()) {
aapsLogger.debug(LTag.APS, "DEFAULT: Closed mode")
return tempBasalRequested || bolusRequested
return tempBasalRequested || bolusRequested()
}
// open loop mode: try to limit request
if (!tempBasalRequested && !bolusRequested) {
if (!tempBasalRequested && !bolusRequested()) {
aapsLogger.debug(LTag.APS, "FALSE: No request")
return false
}
@ -399,4 +397,6 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) {
}
}
}
fun bolusRequested(): Boolean = smb > 0.0
}

View file

@ -82,7 +82,7 @@ class RunningConfiguration @Inject constructor(
if (configuration.has("pump")) {
val pumpType = JsonHelper.safeGetString(configuration, "pump", PumpType.GENERIC_AAPS.description)
sp.putString(R.string.key_virtualpump_type, pumpType)
activePlugin.activePump.pumpDescription.setPumpDescription(PumpType.getByDescription(pumpType))
activePlugin.activePump.pumpDescription.fillFor(PumpType.getByDescription(pumpType))
aapsLogger.debug(LTag.CORE, "Changing pump type to $pumpType")
}

View file

@ -1,92 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import org.json.JSONObject;
import javax.inject.Inject;
import javax.inject.Singleton;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.db.DbRequest;
import info.nightscout.androidaps.interfaces.UploadQueueInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
/**
* Created by mike on 26.05.2017.
*/
@Singleton
public class NSUpload {
private final AAPSLogger aapsLogger;
private final SP sp;
private final UploadQueueInterface uploadQueue;
@Inject
public NSUpload(
AAPSLogger aapsLogger,
SP sp,
UploadQueueInterface uploadQueue
) {
this.aapsLogger = aapsLogger;
this.sp = sp;
this.uploadQueue = uploadQueue;
}
/*
public void uploadProfileSwitch(ProfileSwitch profileSwitch, long nsClientId, DateUtil dateUtil) {
try {
JSONObject data = getJson(profileSwitch, dateUtil);
DbRequest dbr = new DbRequest("dbAdd", "treatments", data, nsClientId);
aapsLogger.debug("Prepared: " + dbr.log());
uploadQueue.add(dbr);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void updateProfileSwitch(ProfileSwitch profileSwitch, DateUtil dateUtil) {
try {
JSONObject data = getJson(profileSwitch, dateUtil);
if (profileSwitch._id != null) {
uploadQueue.add(new DbRequest("dbUpdate", "treatments", profileSwitch._id, data, profileSwitch.date));
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
private static JSONObject getJson(ProfileSwitch profileSwitch, DateUtil dateUtil) throws JSONException {
JSONObject data = new JSONObject();
data.put("eventType", TherapyEvent.Type.PROFILE_SWITCH.getText());
data.put("duration", profileSwitch.durationInMinutes);
data.put("profile", profileSwitch.getCustomizedName());
data.put("profileJson", profileSwitch.profileJson);
data.put("profilePlugin", profileSwitch.profilePlugin);
if (profileSwitch.isCPP) {
data.put("CircadianPercentageProfile", true);
data.put("timeshift", profileSwitch.timeshift);
data.put("percentage", profileSwitch.percentage);
}
data.put("created_at", dateUtil.toISOString(profileSwitch.date));
data.put("enteredBy", "AndroidAPS");
return data;
}
*/
// TODO replace with setting isValid = false
public void removeCareportalEntryFromNS(String _id) {
uploadQueue.add(new DbRequest("dbRemove", "treatments", _id, System.currentTimeMillis()));
}
public void uploadProfileStore(JSONObject profileStore) {
if (sp.getBoolean(R.string.key_ns_uploadlocalprofile, false)) {
uploadQueue.add(new DbRequest("dbAdd", "profile", profileStore, System.currentTimeMillis()));
}
}
public static boolean isIdValid(String _id) {
if (_id == null)
return false;
return _id.length() == 24;
}
}

View file

@ -330,6 +330,18 @@ class PumpSyncImplementation @Inject constructor(
}
}
override fun invalidateTemporaryBasalWithTempId(temporaryId: Long): Boolean {
repository.runTransactionForResult(InvalidateTemporaryBasalWithTempIdTransaction(temporaryId))
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating TemporaryBasal", it) }
.blockingGet()
.also { result ->
result.invalidated.forEach {
aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it")
}
return result.invalidated.size > 0
}
}
override fun syncExtendedBolusWithPumpId(timestamp: Long, amount: Double, duration: Long, isEmulatingTB: Boolean, pumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean {
if (!confirmActivePump(timestamp, pumpType, pumpSerial)) return false
val extendedBolus = ExtendedBolus(

View file

@ -64,7 +64,7 @@ enum class PumpType {
baseBasalStep = 0.1,
baseBasalSpecialSteps = null,
pumpCapability = PumpCapability.VirtualPumpCapabilities),
ACCU_CHEK_INSIGHT(description = "Accu-Chek Insight",
ACCU_CHEK_INSIGHT_VIRTUAL(description = "Accu-Chek Insight",
manufacturer = ManufacturerType.Roche,
model = "Insight",
bolusSize = 0.05,
@ -77,7 +77,7 @@ enum class PumpType {
baseBasalStep = 0.01,
baseBasalSpecialSteps = null,
pumpCapability = PumpCapability.InsightCapabilities),
ACCU_CHEK_INSIGHT_BLUETOOTH(description = "Accu-Chek Insight",
ACCU_CHEK_INSIGHT(description = "Accu-Chek Insight",
manufacturer = ManufacturerType.Roche,
model = "Insight",
bolusSize = 0.01,
@ -89,7 +89,7 @@ enum class PumpType {
baseBasalMinValue = 0.02,
baseBasalMaxValue = null,
baseBasalStep = 0.01,
baseBasalSpecialSteps = DoseStepSize.InsightBolus,
baseBasalSpecialSteps = DoseStepSize.InsightBasal,
pumpCapability = PumpCapability.InsightCapabilities),
ACCU_CHEK_SOLO(description = "Accu-Chek Solo",
manufacturer = ManufacturerType.Roche,
@ -418,8 +418,8 @@ enum class PumpType {
CELLNOVO -> InterfaceIDs.PumpType.CELLNOVO
ACCU_CHEK_COMBO -> InterfaceIDs.PumpType.ACCU_CHEK_COMBO
ACCU_CHEK_SPIRIT -> InterfaceIDs.PumpType.ACCU_CHEK_SPIRIT
ACCU_CHEK_INSIGHT -> InterfaceIDs.PumpType.ACCU_CHEK_INSIGHT
ACCU_CHEK_INSIGHT_BLUETOOTH -> InterfaceIDs.PumpType.ACCU_CHEK_INSIGHT_BLUETOOTH
ACCU_CHEK_INSIGHT_VIRTUAL -> InterfaceIDs.PumpType.ACCU_CHEK_INSIGHT
ACCU_CHEK_INSIGHT -> InterfaceIDs.PumpType.ACCU_CHEK_INSIGHT_BLUETOOTH
ACCU_CHEK_SOLO -> InterfaceIDs.PumpType.ACCU_CHEK_SOLO
ANIMAS_VIBE -> InterfaceIDs.PumpType.ANIMAS_VIBE
ANIMAS_PING -> InterfaceIDs.PumpType.ANIMAS_PING

View file

@ -19,7 +19,6 @@
<string name="key_high_mark" translatable="false">high_mark</string>
<string name="key_ns_create_announcements_from_errors" translatable="false">ns_create_announcements_from_errors</string>
<string name="key_ns_logappstartedevent" translatable="false">ns_logappstartedevent</string>
<string name="key_ns_uploadlocalprofile" translatable="false">ns_uploadlocalprofile</string>
<string name="key_btwatchdog" translatable="false">bt_watchdog</string>
<string name="key_btwatchdog_lastbark" translatable="false">bt_watchdog_last</string>
<string name="key_pump_unreachable_threshold_minutes" translatable="false">pump_unreachable_threshold</string>

View file

@ -10,7 +10,7 @@ class PumpDescriptionTest {
@Test fun setPumpDescription() {
val pumpDescription = PumpDescription()
pumpDescription.setPumpDescription(PumpType.ACCU_CHEK_COMBO)
pumpDescription.fillFor(PumpType.ACCU_CHEK_COMBO)
Assert.assertEquals(pumpDescription.bolusStep, PumpType.ACCU_CHEK_COMBO.bolusSize, 0.1)
Assert.assertEquals(pumpDescription.basalMinimumRate, PumpType.ACCU_CHEK_COMBO.baseBasalStep, 0.1)
Assert.assertEquals(pumpDescription.basalStep, PumpType.ACCU_CHEK_COMBO.baseBasalStep, 0.1)

View file

@ -51,7 +51,7 @@ class APSResultTest : TestBaseWithProfile() {
// BASAL RATE IN TEST PROFILE IS 1U/h
// **** PERCENT pump ****
testPumpPlugin.pumpDescription.setPumpDescription(PumpType.CELLNOVO) // % based
testPumpPlugin.pumpDescription.fillFor(PumpType.CELLNOVO) // % based
apsResult.usePercent(true)
// closed loop mode return original request
@ -109,7 +109,7 @@ class APSResultTest : TestBaseWithProfile() {
Assert.assertEquals(true, apsResult.isChangeRequested)
// **** ABSOLUTE pump ****
testPumpPlugin.pumpDescription.setPumpDescription(PumpType.MEDTRONIC_515_715) // U/h based
testPumpPlugin.pumpDescription.fillFor(PumpType.MEDTRONIC_515_715) // U/h based
apsResult.usePercent(false)
// open loop

View file

@ -12,9 +12,21 @@ android {
defaultConfig {
versionCode 1
versionName "1.0"
kapt {
arguments {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas")
}
}
}
}
dependencies {
implementation project(':core')
api "androidx.room:room-ktx:$room_version"
api "androidx.room:room-runtime:$room_version"
api "androidx.room:room-rxjava2:$room_version"
kapt "androidx.room:room-compiler:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"
}

View file

@ -0,0 +1,92 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "04f2e440fae2d62ca4e4a38840b5cf2f",
"entities": [
{
"tableName": "danaHistory",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `code` INTEGER NOT NULL, `value` REAL NOT NULL, `bolusType` TEXT NOT NULL, `stringValue` TEXT NOT NULL, `duration` INTEGER NOT NULL, `dailyBasal` REAL NOT NULL, `dailyBolus` REAL NOT NULL, `alarm` TEXT NOT NULL, PRIMARY KEY(`timestamp`))",
"fields": [
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "code",
"columnName": "code",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "bolusType",
"columnName": "bolusType",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "stringValue",
"columnName": "stringValue",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dailyBasal",
"columnName": "dailyBasal",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "dailyBolus",
"columnName": "dailyBolus",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "alarm",
"columnName": "alarm",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"timestamp"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_danaHistory_code_timestamp",
"unique": false,
"columnNames": [
"code",
"timestamp"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_danaHistory_code_timestamp` ON `${TABLE_NAME}` (`code`, `timestamp`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '04f2e440fae2d62ca4e4a38840b5cf2f')"
]
}
}

View file

@ -6,21 +6,21 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.dana.R
import info.nightscout.androidaps.dana.comm.RecordTypes
import info.nightscout.androidaps.dana.database.DanaHistoryRecord
import info.nightscout.androidaps.dana.database.DanaHistoryRecordDao
import info.nightscout.androidaps.dana.databinding.DanarHistoryActivityBinding
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.db.DanaRHistoryRecord
import info.nightscout.androidaps.dana.databinding.DanarHistoryItemBinding
import info.nightscout.androidaps.events.EventDanaRSyncStatus
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -30,6 +30,7 @@ import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
@ -44,14 +45,13 @@ class DanaHistoryActivity : NoSplashAppCompatActivity() {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var danaHistoryRecordDao: DanaHistoryRecordDao
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var aapsSchedulers: AapsSchedulers
private val disposable = CompositeDisposable()
private var showingType = RecordTypes.RECORD_TYPE_ALARM
private var historyList: List<DanaRHistoryRecord> = ArrayList()
class TypeList internal constructor(var type: Byte, var name: String) {
@ -73,6 +73,7 @@ class DanaHistoryActivity : NoSplashAppCompatActivity() {
aapsLogger.debug(LTag.PUMP, "EventDanaRSyncStatus: " + it.message)
binding.status.text = it.message
}, fabricPrivacy::logException)
swapAdapter(showingType)
}
override fun onPause() {
@ -87,7 +88,6 @@ class DanaHistoryActivity : NoSplashAppCompatActivity() {
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(this)
binding.recyclerview.adapter = RecyclerViewAdapter(historyList)
binding.status.visibility = View.GONE
val pump = activePlugin.activePump
@ -115,14 +115,12 @@ class DanaHistoryActivity : NoSplashAppCompatActivity() {
binding.reload.setOnClickListener {
val selected = binding.spinner.selectedItem as TypeList?
?: return@setOnClickListener
runOnUiThread {
binding.reload.visibility = View.GONE
binding.status.visibility = View.VISIBLE
}
clearCardView()
commandQueue.loadHistory(selected.type, object : Callback() {
override fun run() {
loadDataFromDB(selected.type)
swapAdapter(selected.type)
runOnUiThread {
binding.reload.visibility = View.VISIBLE
binding.status.visibility = View.GONE
@ -133,7 +131,7 @@ class DanaHistoryActivity : NoSplashAppCompatActivity() {
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val selected = typeList[position]
loadDataFromDB(selected.type)
swapAdapter(selected.type)
showingType = selected.type
}
@ -143,95 +141,95 @@ class DanaHistoryActivity : NoSplashAppCompatActivity() {
}
}
inner class RecyclerViewAdapter internal constructor(private var historyList: List<DanaRHistoryRecord>) : RecyclerView.Adapter<RecyclerViewAdapter.HistoryViewHolder>() {
inner class RecyclerViewAdapter internal constructor(private var historyList: List<DanaHistoryRecord>) : RecyclerView.Adapter<RecyclerViewAdapter.HistoryViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): HistoryViewHolder =
HistoryViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.danar_history_item, viewGroup, false))
override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) {
val record = historyList[position]
holder.time.text = dateUtil.dateAndTimeString(record.recordDate)
holder.value.text = DecimalFormatter.to2Decimal(record.recordValue)
holder.stringValue.text = record.stringRecordValue
holder.bolusType.text = record.bolusType
holder.duration.text = DecimalFormatter.to0Decimal(record.recordDuration.toDouble())
holder.alarm.text = record.recordAlarm
holder.binding.time.text = dateUtil.dateAndTimeString(record.timestamp)
holder.binding.value.text = DecimalFormatter.to2Decimal(record.value)
holder.binding.stringValue.text = record.stringValue
holder.binding.bolusType.text = record.bolusType
holder.binding.duration.text = record.duration.toString()
holder.binding.alarm.text = record.alarm
when (showingType) {
RecordTypes.RECORD_TYPE_ALARM -> {
holder.time.visibility = View.VISIBLE
holder.value.visibility = View.VISIBLE
holder.stringValue.visibility = View.GONE
holder.bolusType.visibility = View.GONE
holder.duration.visibility = View.GONE
holder.dailyBasal.visibility = View.GONE
holder.dailyBolus.visibility = View.GONE
holder.dailyTotal.visibility = View.GONE
holder.alarm.visibility = View.VISIBLE
holder.binding.time.visibility = View.VISIBLE
holder.binding.value.visibility = View.VISIBLE
holder.binding.stringValue.visibility = View.GONE
holder.binding.bolusType.visibility = View.GONE
holder.binding.duration.visibility = View.GONE
holder.binding.dailyBasal.visibility = View.GONE
holder.binding.dailyBolus.visibility = View.GONE
holder.binding.dailyTotal.visibility = View.GONE
holder.binding.alarm.visibility = View.VISIBLE
}
RecordTypes.RECORD_TYPE_BOLUS -> {
holder.time.visibility = View.VISIBLE
holder.value.visibility = View.VISIBLE
holder.stringValue.visibility = View.GONE
holder.bolusType.visibility = View.VISIBLE
holder.duration.visibility = View.VISIBLE
holder.dailyBasal.visibility = View.GONE
holder.dailyBolus.visibility = View.GONE
holder.dailyTotal.visibility = View.GONE
holder.alarm.visibility = View.GONE
holder.binding.time.visibility = View.VISIBLE
holder.binding.value.visibility = View.VISIBLE
holder.binding.stringValue.visibility = View.GONE
holder.binding.bolusType.visibility = View.VISIBLE
holder.binding.duration.visibility = View.VISIBLE
holder.binding.dailyBasal.visibility = View.GONE
holder.binding.dailyBolus.visibility = View.GONE
holder.binding.dailyTotal.visibility = View.GONE
holder.binding.alarm.visibility = View.GONE
}
RecordTypes.RECORD_TYPE_DAILY -> {
holder.dailyBasal.text = resourceHelper.gs(R.string.formatinsulinunits, record.recordDailyBasal)
holder.dailyBolus.text = resourceHelper.gs(R.string.formatinsulinunits, record.recordDailyBolus)
holder.dailyTotal.text = resourceHelper.gs(R.string.formatinsulinunits, record.recordDailyBolus + record.recordDailyBasal)
holder.time.text = dateUtil.dateString(record.recordDate)
holder.time.visibility = View.VISIBLE
holder.value.visibility = View.GONE
holder.stringValue.visibility = View.GONE
holder.bolusType.visibility = View.GONE
holder.duration.visibility = View.GONE
holder.dailyBasal.visibility = View.VISIBLE
holder.dailyBolus.visibility = View.VISIBLE
holder.dailyTotal.visibility = View.VISIBLE
holder.alarm.visibility = View.GONE
holder.binding.dailyBasal.text = resourceHelper.gs(R.string.formatinsulinunits, record.dailyBasal)
holder.binding.dailyBolus.text = resourceHelper.gs(R.string.formatinsulinunits, record.dailyBolus)
holder.binding.dailyTotal.text = resourceHelper.gs(R.string.formatinsulinunits, record.dailyBolus + record.dailyBasal)
holder.binding.time.text = dateUtil.dateString(record.timestamp)
holder.binding.time.visibility = View.VISIBLE
holder.binding.value.visibility = View.GONE
holder.binding.stringValue.visibility = View.GONE
holder.binding.bolusType.visibility = View.GONE
holder.binding.duration.visibility = View.GONE
holder.binding.dailyBasal.visibility = View.VISIBLE
holder.binding.dailyBolus.visibility = View.VISIBLE
holder.binding.dailyTotal.visibility = View.VISIBLE
holder.binding.alarm.visibility = View.GONE
}
RecordTypes.RECORD_TYPE_GLUCOSE -> {
holder.value.text = Profile.toUnitsString(record.recordValue, record.recordValue * Constants.MGDL_TO_MMOLL, profileFunction.getUnits())
holder.time.visibility = View.VISIBLE
holder.value.visibility = View.VISIBLE
holder.stringValue.visibility = View.GONE
holder.bolusType.visibility = View.GONE
holder.duration.visibility = View.GONE
holder.dailyBasal.visibility = View.GONE
holder.dailyBolus.visibility = View.GONE
holder.dailyTotal.visibility = View.GONE
holder.alarm.visibility = View.GONE
holder.binding.value.text = Profile.toUnitsString(record.value, record.value * Constants.MGDL_TO_MMOLL, profileFunction.getUnits())
holder.binding.time.visibility = View.VISIBLE
holder.binding.value.visibility = View.VISIBLE
holder.binding.stringValue.visibility = View.GONE
holder.binding.bolusType.visibility = View.GONE
holder.binding.duration.visibility = View.GONE
holder.binding.dailyBasal.visibility = View.GONE
holder.binding.dailyBolus.visibility = View.GONE
holder.binding.dailyTotal.visibility = View.GONE
holder.binding.alarm.visibility = View.GONE
}
RecordTypes.RECORD_TYPE_CARBO, RecordTypes.RECORD_TYPE_BASALHOUR, RecordTypes.RECORD_TYPE_ERROR, RecordTypes.RECORD_TYPE_PRIME, RecordTypes.RECORD_TYPE_REFILL, RecordTypes.RECORD_TYPE_TB -> {
holder.time.visibility = View.VISIBLE
holder.value.visibility = View.VISIBLE
holder.stringValue.visibility = View.GONE
holder.bolusType.visibility = View.GONE
holder.duration.visibility = View.GONE
holder.dailyBasal.visibility = View.GONE
holder.dailyBolus.visibility = View.GONE
holder.dailyTotal.visibility = View.GONE
holder.alarm.visibility = View.GONE
holder.binding.time.visibility = View.VISIBLE
holder.binding.value.visibility = View.VISIBLE
holder.binding.stringValue.visibility = View.GONE
holder.binding.bolusType.visibility = View.GONE
holder.binding.duration.visibility = View.GONE
holder.binding.dailyBasal.visibility = View.GONE
holder.binding.dailyBolus.visibility = View.GONE
holder.binding.dailyTotal.visibility = View.GONE
holder.binding.alarm.visibility = View.GONE
}
RecordTypes.RECORD_TYPE_SUSPEND -> {
holder.time.visibility = View.VISIBLE
holder.value.visibility = View.GONE
holder.stringValue.visibility = View.VISIBLE
holder.bolusType.visibility = View.GONE
holder.duration.visibility = View.GONE
holder.dailyBasal.visibility = View.GONE
holder.dailyBolus.visibility = View.GONE
holder.dailyTotal.visibility = View.GONE
holder.alarm.visibility = View.GONE
holder.binding.time.visibility = View.VISIBLE
holder.binding.value.visibility = View.GONE
holder.binding.stringValue.visibility = View.VISIBLE
holder.binding.bolusType.visibility = View.GONE
holder.binding.duration.visibility = View.GONE
holder.binding.dailyBasal.visibility = View.GONE
holder.binding.dailyBolus.visibility = View.GONE
holder.binding.dailyTotal.visibility = View.GONE
holder.binding.alarm.visibility = View.GONE
}
}
}
@ -242,25 +240,17 @@ class DanaHistoryActivity : NoSplashAppCompatActivity() {
inner class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var time: TextView = itemView.findViewById(R.id.danar_history_time)
var value: TextView = itemView.findViewById(R.id.danar_history_value)
var bolusType: TextView = itemView.findViewById(R.id.danar_history_bolustype)
var stringValue: TextView = itemView.findViewById(R.id.danar_history_stringvalue)
var duration: TextView = itemView.findViewById(R.id.danar_history_duration)
var dailyBasal: TextView = itemView.findViewById(R.id.danar_history_dailybasal)
var dailyBolus: TextView = itemView.findViewById(R.id.danar_history_dailybolus)
var dailyTotal: TextView = itemView.findViewById(R.id.danar_history_dailytotal)
var alarm: TextView = itemView.findViewById(R.id.danar_history_alarm)
val binding = DanarHistoryItemBinding.bind(itemView)
}
}
private fun loadDataFromDB(type: Byte) {
historyList = databaseHelper.getDanaRHistoryRecordsByType(type)
runOnUiThread { binding.recyclerview.swapAdapter(RecyclerViewAdapter(historyList), false) }
private fun swapAdapter(type: Byte) {
disposable += danaHistoryRecordDao
.allFromByType(dateUtil.now() - T.months(1).msecs(), type)
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribe { historyList -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(historyList), false) }
}
private fun clearCardView() {
historyList = ArrayList()
runOnUiThread { binding.recyclerview.swapAdapter(RecyclerViewAdapter(historyList), false) }
}
private fun clearCardView() = binding.recyclerview.swapAdapter(RecyclerViewAdapter(ArrayList()), false)
}

View file

@ -0,0 +1,33 @@
package info.nightscout.androidaps.dana.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
const val TABLE_DANA_HISTORY = "danaHistory"
@Database(
entities = [DanaHistoryRecord::class],
exportSchema = true,
version = DanaHistoryDatabase.VERSION
)
abstract class DanaHistoryDatabase : RoomDatabase() {
abstract fun historyRecordDao(): DanaHistoryRecordDao
companion object {
const val VERSION = 1
fun build(context: Context) =
Room.databaseBuilder(
context.applicationContext,
DanaHistoryDatabase::class.java,
"dana_database.db"
)
.fallbackToDestructiveMigration()
.build()
}
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.dana.database
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = TABLE_DANA_HISTORY,
indices = [Index("code", "timestamp")])
data class DanaHistoryRecord(
@PrimaryKey var timestamp: Long,
var code: Byte = 0x0F,
var value: Double = 0.0,
var bolusType: String = "None",
var stringValue: String = "",
var duration: Long = 0,
var dailyBasal: Double = 0.0,
var dailyBolus: Double = 0.0,
var alarm: String = ""
)

View file

@ -0,0 +1,17 @@
package info.nightscout.androidaps.dana.database
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.reactivex.Single
@Dao
abstract class DanaHistoryRecordDao {
@Query("SELECT * from $TABLE_DANA_HISTORY WHERE timestamp >= :timestamp AND code = :type")
abstract fun allFromByType(timestamp: Long, type: Byte): Single<List<DanaHistoryRecord>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun createOrUpdate(danaHistoryRecord: DanaHistoryRecord)
}

View file

@ -0,0 +1,21 @@
package info.nightscout.androidaps.dana.di
import android.content.Context
import dagger.Module
import dagger.Provides
import info.nightscout.androidaps.dana.database.DanaHistoryDatabase
import info.nightscout.androidaps.dana.database.DanaHistoryRecordDao
import javax.inject.Singleton
@Module
class DanaHistoryModule {
@Provides
@Singleton
internal fun provideDatabase(context: Context): DanaHistoryDatabase = DanaHistoryDatabase.build(context)
@Provides
@Singleton
internal fun provideHistoryRecordDao(danaHistoryDatabase: DanaHistoryDatabase): DanaHistoryRecordDao =
danaHistoryDatabase.historyRecordDao()
}

View file

@ -1,10 +1,15 @@
package info.nightscout.androidaps.dana.di
import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.dana.DanaFragment
import info.nightscout.androidaps.dana.activities.DanaHistoryActivity
import info.nightscout.androidaps.dana.activities.DanaUserOptionsActivity
import info.nightscout.androidaps.dana.database.DanaHistoryDatabase
import info.nightscout.androidaps.dana.database.DanaHistoryRecordDao
import javax.inject.Singleton
@Module
@Suppress("unused")

View file

@ -1,94 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relativeLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context="info.nightscout.androidaps.dana.activities.DanaHistoryActivity">
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_gravity="center"
android:layout_marginBottom="10dp"
android:background="@color/activity_title_background"
android:orientation="horizontal"
android:padding="5dp">
android:padding="5dp"
app:layout_constraintBottom_toTopOf="@+id/spinner"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/pumphistory"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_danarhistory" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="@string/pumphistory"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
android:textAppearance="?android:attr/textAppearanceLarge"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/spacer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/header"
android:orientation="horizontal"
android:padding="5dp" />
<LinearLayout
android:id="@+id/spinner_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/spacer"
android:orientation="horizontal">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:text="@string/eventtype"
android:textAppearance="?android:attr/textAppearanceSmall" />
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintBottom_toBottomOf="@+id/spinner"
app:layout_constraintEnd_toStartOf="@+id/spinner"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/spinner" />
<Spinner
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp" />
</LinearLayout>
android:layout_marginEnd="10dp"
app:layout_constraintBottom_toTopOf="@+id/status"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView"
app:layout_constraintTop_toBottomOf="@+id/header" />
<TextView
android:id="@+id/status"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/spinner_layout"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal" />
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:gravity="center_horizontal"
app:layout_constraintBottom_toTopOf="@+id/recyclerview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/reload"
android:layout_below="@+id/status" />
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/reload"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status" />
<Button
android:id="@+id/reload"
style="@style/Widget.AppCompat.Button"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="1dp"
android:drawableStart="@drawable/ic_actions_refill"
android:text="@string/reload" />
android:text="@string/reload"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/recyclerview" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,16 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/danar_history_cardview"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardBackgroundColor="@color/cardColorBackground"
card_view:cardCornerRadius="6dp"
card_view:cardUseCompatPadding="true"
card_view:contentPadding="6dp">
card_view:cardBackgroundColor="?android:colorBackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -19,71 +21,89 @@
<TextView
android:id="@+id/danar_history_time"
android:id="@+id/time"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:text="27.06.2016 18:00" />
android:text="27.06.2016 18:00"
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_value"
android:id="@+id/value"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:gravity="right"
android:paddingLeft="5dp"
android:text="0.25"
android:gravity="right" />
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_bolustype"
android:id="@+id/bolus_type"
android:layout_width="20dp"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:text="E"
/>
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_stringvalue"
android:id="@+id/string_value"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:text="XXXXXXX"
/>
android:text=""
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_duration"
android:id="@+id/duration"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:gravity="right"
android:paddingLeft="5dp"
android:text="30"
android:gravity="right" />
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_dailybasal"
android:id="@+id/daily_basal"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:gravity="right"
android:paddingLeft="5dp"
android:gravity="right" />
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_dailybolus"
android:id="@+id/daily_bolus"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:gravity="right"
android:paddingLeft="5dp"
android:gravity="right" />
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_dailytotal"
android:id="@+id/daily_total"
android:layout_width="56dp"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:gravity="right"
android:visibility="gone"/>
android:paddingLeft="10dp"
android:visibility="gone"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/danar_history_alarm"
android:id="@+id/alarm"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:paddingLeft="5dp" />
android:paddingLeft="5dp"
tools:ignore="RtlHardcoded,RtlSymmetry" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="5dp"
android:background="@color/list_delimiter" />
</LinearLayout>

View file

@ -60,7 +60,7 @@ class DanaRKoreanPlugin @Inject constructor(
init {
pluginDescription.description(R.string.description_pump_dana_r_korean)
useExtendedBoluses = sp.getBoolean(R.string.key_danar_useextended, false)
pumpDescription.setPumpDescription(PumpType.DANA_R_KOREAN)
pumpDescription.fillFor(PumpType.DANA_R_KOREAN)
}
override fun onStart() {

View file

@ -194,7 +194,6 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService {
rxBus.send(new EventDanaRNewStatus());
rxBus.send(new EventInitializationChanged());
//NSUpload.uploadDeviceStatus();
if (danaPump.getDailyTotalUnits() > danaPump.getMaxDailyTotalUnits() * Constants.dailyLimitWarning) {
aapsLogger.debug(LTag.PUMP, "Approaching daily limit: " + danaPump.getDailyTotalUnits() + "/" + danaPump.getMaxDailyTotalUnits());
if (System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) {

View file

@ -85,7 +85,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin {
getPluginDescription().description(R.string.description_pump_dana_r_v2);
useExtendedBoluses = false;
pumpDescription.setPumpDescription(PumpType.DANA_RV2);
pumpDescription.fillFor(PumpType.DANA_RV2);
}
@Override

View file

@ -231,7 +231,6 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService {
rxBus.send(new EventDanaRNewStatus());
rxBus.send(new EventInitializationChanged());
//NSUpload.uploadDeviceStatus();
if (danaPump.getDailyTotalUnits() > danaPump.getMaxDailyTotalUnits() * Constants.dailyLimitWarning) {
aapsLogger.debug(LTag.PUMP, "Approaching daily limit: " + danaPump.getDailyTotalUnits() + "/" + danaPump.getMaxDailyTotalUnits());
if (System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) {

View file

@ -74,7 +74,7 @@ public class DanaRPlugin extends AbstractDanaRPlugin {
this.fabricPrivacy = fabricPrivacy;
useExtendedBoluses = sp.getBoolean(R.string.key_danar_useextended, false);
pumpDescription.setPumpDescription(PumpType.DANA_R);
pumpDescription.fillFor(PumpType.DANA_R);
}
@Override

Some files were not shown because too many files have changed in this diff Show more