Merge remote-tracking branch 'Nightscout/dev' into UserEntryImprovement

This commit is contained in:
Philoul 2021-03-21 15:29:48 +01:00
commit 4f5132d189
77 changed files with 4686 additions and 2147 deletions

View file

@ -2,22 +2,6 @@
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="AUTODETECT_INDENTS" value="false" /> <option name="AUTODETECT_INDENTS" value="false" />
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" /> <option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" />

View file

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

View file

@ -295,7 +295,7 @@ class MainActivity : NoSplashAppCompatActivity() {
R.id.nav_about -> { R.id.nav_about -> {
var message = "Build: ${BuildConfig.BUILDVERSION}\n" var message = "Build: ${BuildConfig.BUILDVERSION}\n"
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n" message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}" message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.getVersion()}"
if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}" if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}"
if (!fabricPrivacy.fabricEnabled()) message += "\n${resourceHelper.gs(R.string.fabric_upload_disabled)}" if (!fabricPrivacy.fabricEnabled()) message += "\n${resourceHelper.gs(R.string.fabric_upload_disabled)}"
message += resourceHelper.gs(R.string.about_link_urls) message += resourceHelper.gs(R.string.about_link_urls)

View file

@ -5,7 +5,6 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.j256.ormlite.android.apptools.OpenHelperManager import com.j256.ormlite.android.apptools.OpenHelperManager
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DaggerApplication import dagger.android.DaggerApplication
@ -24,11 +23,9 @@ import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionChec
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.receivers.BTReceiver import info.nightscout.androidaps.receivers.BTReceiver
import info.nightscout.androidaps.receivers.ChargingStateReceiver import info.nightscout.androidaps.receivers.ChargingStateReceiver
import info.nightscout.androidaps.receivers.DataReceiver
import info.nightscout.androidaps.receivers.KeepAliveReceiver.KeepAliveManager import info.nightscout.androidaps.receivers.KeepAliveReceiver.KeepAliveManager
import info.nightscout.androidaps.receivers.NetworkChangeReceiver import info.nightscout.androidaps.receivers.NetworkChangeReceiver
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.locale.LocaleHelper.update import info.nightscout.androidaps.utils.locale.LocaleHelper.update
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -102,15 +99,6 @@ class MainApp : DaggerApplication() {
private fun registerLocalBroadcastReceiver() { private fun registerLocalBroadcastReceiver() {
var filter = IntentFilter() var filter = IntentFilter()
filter.addAction(Intents.ACTION_NEW_TREATMENT)
filter.addAction(Intents.ACTION_CHANGED_TREATMENT)
filter.addAction(Intents.ACTION_REMOVED_TREATMENT)
filter.addAction(Intents.ACTION_NEW_SGV)
filter.addAction(Intents.ACTION_NEW_PROFILE)
filter.addAction(Intents.ACTION_NEW_MBG)
filter.addAction(Intents.ACTION_NEW_CAL)
LocalBroadcastManager.getInstance(this).registerReceiver(DataReceiver(), filter)
filter = IntentFilter()
filter.addAction(Intent.ACTION_TIME_CHANGED) filter.addAction(Intent.ACTION_TIME_CHANGED)
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED) filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
registerReceiver(TimeDateOrTZChangeReceiver(), filter) registerReceiver(TimeDateOrTZChangeReceiver(), filter)

View file

@ -1,9 +1,11 @@
package info.nightscout.androidaps.db package info.nightscout.androidaps.db
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.Food
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.events.EventFoodDatabaseChanged
import info.nightscout.androidaps.events.EventNewBG import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.events.EventTherapyEventChange import info.nightscout.androidaps.events.EventTherapyEventChange
@ -44,5 +46,9 @@ class CompatDBHelper @Inject constructor(
aapsLogger.debug(LTag.DATABASE, "Firing EventTherapyEventChange") aapsLogger.debug(LTag.DATABASE, "Firing EventTherapyEventChange")
rxBus.send(EventTherapyEventChange()) rxBus.send(EventTherapyEventChange())
} }
it.filterIsInstance<Food>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventFoodDatabaseChanged")
rxBus.send(EventFoodDatabaseChanged())
}
} }
} }

View file

@ -67,6 +67,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
@Inject RxBusWrapper rxBus; @Inject RxBusWrapper rxBus;
@Inject VirtualPumpPlugin virtualPumpPlugin; @Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject OpenHumansUploader openHumansUploader; @Inject OpenHumansUploader openHumansUploader;
@Inject ActivePluginProvider activePlugin;
@Inject NSUpload nsUpload;
public static final String DATABASE_NAME = "AndroidAPSDb"; public static final String DATABASE_NAME = "AndroidAPSDb";
public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses"; public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses";
@ -1216,7 +1218,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
} }
*/ */
public void createProfileSwitchFromJsonIfNotExists(ActivePluginProvider activePluginProvider, NSUpload nsUpload, JSONObject trJson) { public void createProfileSwitchFromJsonIfNotExists(JSONObject trJson) {
try { try {
ProfileSwitch profileSwitch = new ProfileSwitch(StaticInjector.Companion.getInstance()); ProfileSwitch profileSwitch = new ProfileSwitch(StaticInjector.Companion.getInstance());
profileSwitch.date = trJson.getLong("mills"); profileSwitch.date = trJson.getLong("mills");
@ -1233,7 +1235,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
if (trJson.has("profileJson")) if (trJson.has("profileJson"))
profileSwitch.profileJson = trJson.getString("profileJson"); profileSwitch.profileJson = trJson.getString("profileJson");
else { else {
ProfileInterface profileInterface = activePluginProvider.getActiveProfileInterface(); ProfileInterface profileInterface = activePlugin.getActiveProfileInterface();
ProfileStore store = profileInterface.getProfile(); ProfileStore store = profileInterface.getProfile();
if (store != null) { if (store != null) {
Profile profile = store.getSpecificProfile(profileSwitch.profileName); Profile profile = store.getSpecificProfile(profileSwitch.profileName);

View file

@ -14,9 +14,7 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
@Singleton @Singleton
public class DatabaseHelperProvider implements DatabaseHelperInterface { public class DatabaseHelperProvider implements DatabaseHelperInterface {
@ -172,8 +170,8 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface {
MainApp.Companion.getDbHelper().createExtendedBolusFromJsonIfNotExists(json); MainApp.Companion.getDbHelper().createExtendedBolusFromJsonIfNotExists(json);
} }
@Override public void createProfileSwitchFromJsonIfNotExists(@NonNull ActivePluginProvider activePluginProvider, @NonNull NSUpload nsUpload, @NonNull JSONObject trJson) { @Override public void createProfileSwitchFromJsonIfNotExists(@NonNull JSONObject trJson) {
MainApp.Companion.getDbHelper().createProfileSwitchFromJsonIfNotExists(activePluginProvider, nsUpload, trJson); MainApp.Companion.getDbHelper().createProfileSwitchFromJsonIfNotExists(trJson);
} }
@Override public void resetDatabases() { @Override public void resetDatabases() {

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.db.DatabaseHelper import info.nightscout.androidaps.db.DatabaseHelper
import info.nightscout.androidaps.plugins.general.food.FoodService
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.treatments.TreatmentService import info.nightscout.androidaps.plugins.treatments.TreatmentService
import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.BolusWizard
@ -17,7 +16,6 @@ abstract class DataClassesModule {
@ContributesAndroidInjector abstract fun DatabaseHelperInjector(): DatabaseHelper @ContributesAndroidInjector abstract fun DatabaseHelperInjector(): DatabaseHelper
@ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService @ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService
@ContributesAndroidInjector abstract fun foodServiceInjector(): FoodService
@ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard @ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard
@ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry @ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry

View file

@ -2,7 +2,10 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker
import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.source.* import info.nightscout.androidaps.plugins.source.*
@ -21,5 +24,8 @@ abstract class WorkersModule {
@ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker @ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker @ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker @ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker
@ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientWorker @ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientAddUpdateWorker
@ContributesAndroidInjector abstract fun contributesNSClientRemoveWorker(): NSClientRemoveWorker
@ContributesAndroidInjector abstract fun contributesNSClientMbgWorker(): NSClientMbgWorker
@ContributesAndroidInjector abstract fun contributesFoodWorker(): FoodPlugin.FoodWorker
} }

View file

@ -1,20 +0,0 @@
package info.nightscout.androidaps.events
import org.json.JSONArray
/**
* Event which is published with data fetched from NightScout specific for the
* Food-class.
*
* Payload is the from NS retrieved JSON-String which should be handled by all
* subscriber.
*/
class EventNsFood(val mode: Int, val foods: JSONArray) : Event() {
companion object {
val ADD = 0
val UPDATE = 1
val REMOVE = 2
}
}

View file

@ -163,7 +163,7 @@ class DataBroadcastPlugin @Inject constructor(
bundle.putString("enacted", loopPlugin.lastRun?.request?.json().toString()) bundle.putString("enacted", loopPlugin.lastRun?.request?.json().toString())
} }
} else { //NSClient or remote } else { //NSClient or remote
val data = NSDeviceStatus.deviceStatusOpenAPSData val data = nsDeviceStatus.deviceStatusOpenAPSData
if (data.clockSuggested != 0L && data.suggested != null) { if (data.clockSuggested != 0L && data.suggested != null) {
bundle.putLong("suggestedTimeStamp", data.clockSuggested) bundle.putLong("suggestedTimeStamp", data.clockSuggested)
bundle.putString("suggested", data.suggested.toString()) bundle.putString("suggested", data.suggested.toString())

View file

@ -1,145 +0,0 @@
package info.nightscout.androidaps.plugins.general.food;
import androidx.annotation.NonNull;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Objects;
import info.nightscout.androidaps.utils.JsonHelper;
/**
* Created by mike on 20.09.2017.
*/
@DatabaseTable(tableName = Food.TABLE_FOODS)
public class Food {
static final String TABLE_FOODS = "Foods";
@DatabaseField(id = true)
public long key;
@DatabaseField
public boolean isValid = true;
@DatabaseField
public String _id; // NS _id
@DatabaseField
public String name;
@DatabaseField
public String category;
@DatabaseField
public String subcategory;
// Example:
// name="juice" portion=250 units="ml" carbs=12
// means 250ml of juice has 12g of carbs
@DatabaseField
public double portion; // common portion in "units"
@DatabaseField
public int carbs; // in grams
@DatabaseField
public int fat = 0; // in grams
@DatabaseField
public int protein = 0; // in grams
@DatabaseField
public int energy = 0; // in kJ
@DatabaseField
public String units = "g";
@DatabaseField
public int gi; // not used yet
private Food() {
key = System.currentTimeMillis();
}
public static Food createFromJson(JSONObject json) throws JSONException {
Food food = new Food();
if ("food".equals(JsonHelper.safeGetString(json, "type"))) {
food._id = JsonHelper.safeGetString(json, "_id");
food.category = JsonHelper.safeGetString(json, "category");
food.subcategory = JsonHelper.safeGetString(json, "subcategory");
food.name = JsonHelper.safeGetString(json, "name");
food.units = JsonHelper.safeGetString(json, "unit");
food.portion = JsonHelper.safeGetDouble(json, "portion");
food.carbs = JsonHelper.safeGetInt(json, "carbs");
food.gi = JsonHelper.safeGetInt(json, "gi");
food.energy = JsonHelper.safeGetInt(json, "energy");
food.protein = JsonHelper.safeGetInt(json, "protein");
food.fat = JsonHelper.safeGetInt(json, "fat");
}
return food;
}
public boolean isEqual(Food other) {
if (portion != other.portion)
return false;
if (carbs != other.carbs)
return false;
if (fat != other.fat)
return false;
if (protein != other.protein)
return false;
if (energy != other.energy)
return false;
if (gi != other.gi)
return false;
if (!Objects.equals(_id, other._id))
return false;
if (!Objects.equals(name, other.name))
return false;
if (!Objects.equals(category, other.category))
return false;
if (!Objects.equals(subcategory, other.subcategory))
return false;
return Objects.equals(units, other.units);
}
public void copyFrom(Food other) {
isValid = other.isValid;
_id = other._id;
name = other.name;
category = other.category;
subcategory = other.subcategory;
portion = other.portion;
carbs = other.carbs;
fat = other.fat;
protein = other.protein;
energy = other.energy;
units = other.units;
gi = other.gi;
}
@Override @NonNull
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("_id=" + _id + ";");
sb.append("isValid=" + isValid + ";");
sb.append("name=" + name + ";");
sb.append("category=" + category + ";");
sb.append("subcategory=" + subcategory + ";");
sb.append("portion=" + portion + ";");
sb.append("carbs=" + carbs + ";");
sb.append("protein=" + protein + ";");
sb.append("energy=" + energy + ";");
sb.append("units=" + units + ";");
sb.append("gi=" + gi + ";");
return sb.toString();
}
}

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.general.food package info.nightscout.androidaps.plugins.general.food
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Paint import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
@ -15,20 +14,30 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.* import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.Food
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.transactions.InvalidateFoodTransaction
import info.nightscout.androidaps.databinding.FoodFragmentBinding import info.nightscout.androidaps.databinding.FoodFragmentBinding
import info.nightscout.androidaps.databinding.FoodItemBinding import info.nightscout.androidaps.databinding.FoodItemBinding
import info.nightscout.androidaps.events.EventFoodDatabaseChanged import info.nightscout.androidaps.events.EventFoodDatabaseChanged
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.food.FoodFragment.RecyclerViewAdapter.FoodsViewHolder
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -36,10 +45,11 @@ class FoodFragment : DaggerFragment() {
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@ -62,8 +72,23 @@ class FoodFragment : DaggerFragment() {
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.adapter = RecyclerViewAdapter(foodPlugin.service?.foodData
?: ArrayList()) binding.refreshFromNightscout.setOnClickListener {
context?.let { context ->
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.refresheventsfromnightscout) + " ?", {
uel.log(Action.FOOD_FROM_NS)
disposable += Completable.fromAction { repository.deleteAllFoods() }
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error removing foods", it) },
onComplete = { rxBus.send(EventFoodDatabaseChanged()) }
)
rxBus.send(EventNSClientRestart())
})
}
}
binding.clearfilter.setOnClickListener { binding.clearfilter.setOnClickListener {
binding.filter.setText("") binding.filter.setText("")
@ -99,10 +124,6 @@ class FoodFragment : DaggerFragment() {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
}) })
loadData()
fillCategories()
fillSubcategories()
filterData()
} }
@Synchronized @Synchronized
@ -111,9 +132,23 @@ class FoodFragment : DaggerFragment() {
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventFoodDatabaseChanged::class.java) .toObservable(EventFoodDatabaseChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateGui() }, fabricPrivacy::logException) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
) )
updateGui() swapAdapter()
}
private fun swapAdapter() {
disposable += repository
.getFoodData()
.observeOn(aapsSchedulers.main)
.subscribe { list ->
unfiltered = list
fillCategories()
fillSubcategories()
filterData()
binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true)
}
} }
@Synchronized @Synchronized
@ -128,14 +163,11 @@ class FoodFragment : DaggerFragment() {
_binding = null _binding = null
} }
private fun loadData() {
unfiltered = foodPlugin.service?.foodData ?: ArrayList()
}
private fun fillCategories() { private fun fillCategories() {
val catSet: MutableSet<CharSequence> = HashSet() val catSet: MutableSet<CharSequence> = HashSet()
for (f in unfiltered) { for (f in unfiltered) {
if (f.category != null && f.category != "") catSet.add(f.category) val category = f.category
if (!category.isNullOrBlank()) catSet.add(category)
} }
// make it unique // make it unique
val categories = ArrayList(catSet) val categories = ArrayList(catSet)
@ -151,7 +183,10 @@ class FoodFragment : DaggerFragment() {
val subCatSet: MutableSet<CharSequence> = HashSet() val subCatSet: MutableSet<CharSequence> = HashSet()
if (categoryFilter != resourceHelper.gs(R.string.none)) { if (categoryFilter != resourceHelper.gs(R.string.none)) {
for (f in unfiltered) { for (f in unfiltered) {
if (f.category != null && f.category == categoryFilter) if (f.subcategory != null && f.subcategory != "") subCatSet.add(f.subcategory) if (f.category != null && f.category == categoryFilter) {
val subCategory = f.subCategory
if (!subCategory.isNullOrEmpty()) subCatSet.add(subCategory)
}
} }
} }
// make it unique // make it unique
@ -165,25 +200,23 @@ class FoodFragment : DaggerFragment() {
private fun filterData() { private fun filterData() {
val textFilter = binding.filter.text.toString() val textFilter = binding.filter.text.toString()
val categoryFilter = binding.category.selectedItem.toString() val categoryFilter = binding.category.selectedItem?.toString() ?: resourceHelper.gs(R.string.none)
val subcategoryFilter = binding.subcategory.selectedItem.toString() val subcategoryFilter = binding.subcategory.selectedItem?.toString() ?: resourceHelper.gs(R.string.none)
val newFiltered = ArrayList<Food>() val newFiltered = ArrayList<Food>()
for (f in unfiltered) { for (f in unfiltered) {
if (f.name == null || f.category == null || f.subcategory == null) continue if (f.category == null || f.subCategory == null) continue
if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subcategory != subcategoryFilter) continue if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subCategory != subcategoryFilter) continue
if (categoryFilter != resourceHelper.gs(R.string.none) && f.category != categoryFilter) continue if (categoryFilter != resourceHelper.gs(R.string.none) && f.category != categoryFilter) continue
if (textFilter != "" && !f.name.toLowerCase(Locale.getDefault()).contains(textFilter.toLowerCase(Locale.getDefault()))) continue if (textFilter != "" && !f.name.toLowerCase(Locale.getDefault()).contains(textFilter.toLowerCase(Locale.getDefault()))) continue
newFiltered.add(f) newFiltered.add(f)
} }
filtered = newFiltered filtered = newFiltered
updateGui()
}
private fun updateGui() {
binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true) binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true)
} }
inner class RecyclerViewAdapter internal constructor(var foodList: List<Food>) : RecyclerView.Adapter<FoodsViewHolder>() { fun Int?.isNotZero(): Boolean = this != null && this != 0
inner class RecyclerViewAdapter internal constructor(private var foodList: List<Food>) : RecyclerView.Adapter<RecyclerViewAdapter.FoodsViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoodsViewHolder { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoodsViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.food_item, viewGroup, false) val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.food_item, viewGroup, false)
@ -193,16 +226,16 @@ class FoodFragment : DaggerFragment() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: FoodsViewHolder, position: Int) { override fun onBindViewHolder(holder: FoodsViewHolder, position: Int) {
val food = foodList[position] val food = foodList[position]
holder.binding.nsSign.visibility = if (food._id != null) View.VISIBLE else View.GONE holder.binding.nsSign.visibility = (food.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.name.text = food.name holder.binding.name.text = food.name
holder.binding.portion.text = food.portion.toString() + food.units holder.binding.portion.text = food.portion.toString() + food.unit
holder.binding.carbs.text = food.carbs.toString() + resourceHelper.gs(R.string.shortgramm) holder.binding.carbs.text = food.carbs.toString() + resourceHelper.gs(R.string.shortgramm)
holder.binding.fat.text = resourceHelper.gs(R.string.shortfat) + ": " + food.fat + resourceHelper.gs(R.string.shortgramm) holder.binding.fat.text = resourceHelper.gs(R.string.shortfat) + ": " + food.fat + resourceHelper.gs(R.string.shortgramm)
if (food.fat == 0) holder.binding.fat.visibility = View.INVISIBLE holder.binding.fat.visibility = food.fat.isNotZero().toVisibility()
holder.binding.protein.text = resourceHelper.gs(R.string.shortprotein) + ": " + food.protein + resourceHelper.gs(R.string.shortgramm) holder.binding.protein.text = resourceHelper.gs(R.string.shortprotein) + ": " + food.protein + resourceHelper.gs(R.string.shortgramm)
if (food.protein == 0) holder.binding.protein.visibility = View.INVISIBLE holder.binding.protein.visibility = food.protein.isNotZero().toVisibility()
holder.binding.energy.text = resourceHelper.gs(R.string.shortenergy) + ": " + food.energy + resourceHelper.gs(R.string.shortkilojoul) holder.binding.energy.text = resourceHelper.gs(R.string.shortenergy) + ": " + food.energy + resourceHelper.gs(R.string.shortkilojoul)
if (food.energy == 0) holder.binding.energy.visibility = View.INVISIBLE holder.binding.energy.visibility = food.energy.isNotZero().toVisibility()
holder.binding.remove.tag = food holder.binding.remove.tag = food
} }
@ -216,12 +249,17 @@ class FoodFragment : DaggerFragment() {
binding.remove.setOnClickListener { v: View -> binding.remove.setOnClickListener { v: View ->
val food = v.tag as Food val food = v.tag as Food
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirmation), resourceHelper.gs(R.string.removerecord) + "\n" + food.name, DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + "\n" + food.name, {
uel.log(Action.FOOD_REMOVED, food.name) uel.log(Action.FOOD_REMOVED, food.name)
if (food._id != null && food._id != "") { disposable += repository.runTransactionForResult(InvalidateFoodTransaction(food.id))
nsUpload.removeFoodFromNS(food._id) .subscribe({
} val id = food.interfaceIDs.nightscoutId
foodPlugin.service?.delete(food) if (NSUpload.isIdValid(id)) nsUpload.removeFoodFromNS(id)
// no create at the moment
// else uploadQueue.removeID("dbAdd", food.timestamp.toString())
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while invalidating food", it)
})
}, null) }, null)
} }
} }

View file

@ -1,12 +1,25 @@
package info.nightscout.androidaps.plugins.general.food package info.nightscout.androidaps.plugins.general.food
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.Food
import info.nightscout.androidaps.database.transactions.SyncFoodTransaction
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.extensions.foodFromJson
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -25,10 +38,76 @@ class FoodPlugin @Inject constructor(
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
) { ) {
var service: FoodService? = null // cannot be inner class because of needed injection
class FoodWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
override fun onStart() { @Inject lateinit var injector: HasAndroidInjector
super.onStart() @Inject lateinit var aapsLogger: AAPSLogger
service = FoodService(injector) @Inject lateinit var repository: AppRepository
@Inject lateinit var sp: SP
@Inject lateinit var dataWorker: DataWorker
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
override fun doWork(): Result {
val foods = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure()
aapsLogger.debug(LTag.DATAFOOD, "Received Food Data: $foods")
var ret = Result.success()
for (index in 0 until foods.length()) {
val jsonFood: JSONObject = foods.getJSONObject(index)
if (JsonHelper.safeGetString(jsonFood, "type") != "food") continue
when (JsonHelper.safeGetString(jsonFood, "action")) {
"remove" -> {
val delFood = Food(
name = "",
portion = 0.0,
carbs = 0,
isValid = false
).also { it.interfaceIDs.nightscoutId = JsonHelper.safeGetString(jsonFood, "_id") }
repository.runTransactionForResult(SyncFoodTransaction(delFood))
.doOnError {
aapsLogger.error(LTag.DATAFOOD, "Error while removing food", it)
ret = Result.failure()
}
.blockingGet()
.also {
it.invalidated.forEach { f -> aapsLogger.debug(LTag.DATAFOOD, "Invalidated food ${f.interfaceIDs.nightscoutId}") }
}
}
else -> {
val food = foodFromJson(jsonFood)
if (food != null) {
repository.runTransactionForResult(SyncFoodTransaction(food))
.doOnError {
aapsLogger.error(LTag.DATAFOOD, "Error while adding/updating food", it)
ret = Result.failure()
}
.blockingGet()
.also { result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATAFOOD, "Inserted food $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Updated food $it") }
result.invalidated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Invalidated food $it") }
}
} else {
aapsLogger.error(LTag.DATAFOOD, "Error parsing food", jsonFood.toString())
ret = Result.failure()
}
}
}
}
return ret
}
} }
} }

View file

@ -1,359 +0,0 @@
package info.nightscout.androidaps.plugins.general.food;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.j256.ormlite.android.apptools.OpenHelperManager;
import com.j256.ormlite.android.apptools.OrmLiteBaseService;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.ICallback;
import info.nightscout.androidaps.events.Event;
import info.nightscout.androidaps.events.EventFoodDatabaseChanged;
import info.nightscout.androidaps.events.EventNsFood;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
import io.reactivex.disposables.CompositeDisposable;
/**
* Created by mike on 24.09.2017.
*/
public class FoodService extends OrmLiteBaseService<DatabaseHelper> {
@Inject AAPSLogger aapsLogger;
@Inject RxBusWrapper rxBus;
@Inject FabricPrivacy fabricPrivacy;
@Inject AapsSchedulers aapsSchedulers;
private final CompositeDisposable disposable = new CompositeDisposable();
private static final ScheduledExecutorService foodEventWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledFoodEventPost = null;
public FoodService(HasAndroidInjector injector) {
injector.androidInjector().inject(this);
onCreate();
dbInitialize();
disposable.add(rxBus
.toObservable(EventNsFood.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
int mode = event.getMode();
JSONArray array = event.getFoods();
if (mode == EventNsFood.Companion.getADD() || mode == EventNsFood.Companion.getUPDATE())
this.createFoodFromJsonIfNotExists(array);
else
this.deleteNS(array);
}, fabricPrivacy::logException)
);
}
/**
* This method is a simple re-implementation of the database create and up/downgrade functionality
* in SQLiteOpenHelper#getDatabaseLocked method.
* <p>
* It is implemented to be able to late initialize separate plugins of the application.
*/
protected void dbInitialize() {
DatabaseHelper helper = OpenHelperManager.getHelper(this, DatabaseHelper.class);
int newVersion = helper.getNewVersion();
int oldVersion = helper.getOldVersion();
if (oldVersion > newVersion) {
onDowngrade(this.getConnectionSource(), oldVersion, newVersion);
} else {
onUpgrade(this.getConnectionSource(), oldVersion, newVersion);
}
}
public Dao<Food, Long> getDao() {
try {
return DaoManager.createDao(this.getConnectionSource(), Food.class);
} catch (SQLException e) {
aapsLogger.error("Cannot create Dao for Food.class");
}
return null;
}
@Override
public void onCreate() {
super.onCreate();
try {
aapsLogger.info(LTag.DATAFOOD, "onCreate");
TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class);
} catch (SQLException e) {
aapsLogger.error("Can't create database", e);
throw new RuntimeException(e);
}
}
public void onUpgrade(ConnectionSource connectionSource, int oldVersion, int newVersion) {
aapsLogger.info(LTag.DATAFOOD, "onUpgrade");
// this.resetFood();
}
public void onDowngrade(ConnectionSource connectionSource, int oldVersion, int newVersion) {
// this method is not supported right now
}
public void resetFood() {
try {
TableUtils.dropTable(this.getConnectionSource(), Food.class, true);
TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class);
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
scheduleFoodChange();
}
/**
* A place to centrally register events to be posted, if any data changed.
* This should be implemented in an abstract service-class.
* <p>
* We do need to make sure, that ICallback is extended to be able to handle multiple
* events, or handle a list of events.
* <p>
* on some methods the earliestDataChange event is handled separatly, in that it is checked if it is
* set to null by another event already (eg. scheduleExtendedBolusChange).
*
* @param event
* @param eventWorker
* @param callback
*/
private void scheduleEvent(final Event event, ScheduledExecutorService eventWorker,
final ICallback callback) {
class PostRunnable implements Runnable {
public void run() {
aapsLogger.debug(LTag.DATAFOOD, "Firing EventFoodChange");
rxBus.send(event);
callback.setPost(null);
}
}
// prepare task for execution in 1 sec
// cancel waiting task to prevent sending multiple posts
if (callback.getPost() != null)
callback.getPost().cancel(false);
Runnable task = new PostRunnable();
final int sec = 1;
callback.setPost(eventWorker.schedule(task, sec, TimeUnit.SECONDS));
}
/**
* Schedule a foodChange Event.
*/
public void scheduleFoodChange() {
this.scheduleEvent(new EventFoodDatabaseChanged(), foodEventWorker, new ICallback() {
@Override
public void setPost(ScheduledFuture<?> post) {
scheduledFoodEventPost = post;
}
@Override
public ScheduledFuture<?> getPost() {
return scheduledFoodEventPost;
}
});
}
public List<Food> getFoodData() {
try {
return this.getDao().queryForAll();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<>();
}
/*
{
"_id": "551ee3ad368e06e80856e6a9",
"type": "food",
"category": "Zakladni",
"subcategory": "Napoje",
"name": "Mleko",
"portion": 250,
"carbs": 12,
"gi": 1,
"created_at": "2015-04-14T06:59:16.500Z",
"unit": "ml"
}
*/
public void createFoodFromJsonIfNotExists(JSONObject json) {
try {
Food food = Food.createFromJson(json);
this.createFoodFromJsonIfNotExists(food);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void createFoodFromJsonIfNotExists(JSONArray array) {
try {
for (int n = 0; n < array.length(); n++) {
JSONObject json = array.getJSONObject(n);
Food food = Food.createFromJson(json);
this.createFoodFromJsonIfNotExists(food);
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void createFoodFromJsonIfNotExists(Food food) {
this.createOrUpdateByNS(food);
}
public void deleteNS(JSONObject json) {
try {
String _id = json.getString("_id");
this.deleteByNSId(_id);
} catch (JSONException | SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void deleteNS(JSONArray array) {
try {
for (int n = 0; n < array.length(); n++) {
JSONObject json = array.getJSONObject(n);
this.deleteNS(json);
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
/**
* deletes an entry by its NS Id.
* <p>
* Basically a convenience method for findByNSId and delete.
*
* @param _id
*/
public void deleteByNSId(String _id) throws SQLException {
Food stored = this.findByNSId(_id);
if (stored != null) {
aapsLogger.debug(LTag.DATAFOOD, "Removing Food record from database: " + stored.toString());
this.delete(stored);
}
}
/**
* deletes the food and sends the foodChange Event
* <p>
* should be moved ot a Service
*
* @param food
*/
public void delete(Food food) {
try {
this.getDao().delete(food);
this.scheduleFoodChange();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
/**
* Create of update a food record by the NS (Nightscout) Id.
*
* @param food
* @return
*/
public boolean createOrUpdateByNS(Food food) {
// find by NS _id
if (food._id != null && !food._id.equals("")) {
Food old = this.findByNSId(food._id);
if (old != null) {
if (!old.isEqual(food)) {
this.delete(old); // need to delete/create because date may change too
old.copyFrom(food);
this.create(old);
return true;
} else {
return false;
}
} else {
this.createOrUpdate(food);
return true;
}
}
return false;
}
public void createOrUpdate(Food food) {
try {
this.getDao().createOrUpdate(food);
aapsLogger.debug(LTag.DATAFOOD, "Created or Updated: " + food.toString());
} catch (SQLException e) {
aapsLogger.error("Unable to createOrUpdate Food", e);
}
this.scheduleFoodChange();
}
public void create(Food food) {
try {
this.getDao().create(food);
aapsLogger.debug(LTag.DATAFOOD, "New record: " + food.toString());
} catch (SQLException e) {
aapsLogger.error("Unable to create Food", e);
}
this.scheduleFoodChange();
}
/**
* finds food by its NS Id.
*
* @param _id
* @return
*/
@Nullable
public Food findByNSId(String _id) {
try {
List<Food> list = this.getDao().queryForEq("_id", _id);
if (list.size() == 1) { // really? if there are more then one result, then we do not return anything...
return list.get(0);
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return null;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View file

@ -108,6 +108,7 @@ class ImportExportPrefs @Inject constructor(
return metadata return metadata
} }
@Suppress("SpellCheckingInspection")
private fun detectUserName(context: Context): String { private fun detectUserName(context: Context): String {
// based on https://medium.com/@pribble88/how-to-get-an-android-device-nickname-4b4700b3068c // based on https://medium.com/@pribble88/how-to-get-an-android-device-nickname-4b4700b3068c
val n1 = Settings.System.getString(context.contentResolver, "bluetooth_name") val n1 = Settings.System.getString(context.contentResolver, "bluetooth_name")
@ -346,7 +347,7 @@ class ImportExportPrefs @Inject constructor(
private fun restartAppAfterImport(context: Context) { private fun restartAppAfterImport(context: Context) {
sp.putBoolean(R.string.key_setupwizard_processed, true) sp.putBoolean(R.string.key_setupwizard_processed, true)
OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable { OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp)) {
uel.log(Action.IMPORT_SETTINGS) uel.log(Action.IMPORT_SETTINGS)
log.debug(LTag.CORE, "Exiting") log.debug(LTag.CORE, "Exiting")
rxBus.send(EventAppExit()) rxBus.send(EventAppExit())
@ -355,14 +356,13 @@ class ImportExportPrefs @Inject constructor(
} }
System.runFinalization() System.runFinalization()
exitProcess(0) exitProcess(0)
}) }
} }
override fun exportUserEntriesCsv(activity: FragmentActivity, listEntries: Single<List<UserEntry>>) { override fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>) {
val entries = listEntries.blockingGet() val entries = singleEntries.blockingGet()
prefFileList.ensureExportDirExists() prefFileList.ensureExportDirExists()
val newFile = prefFileList.newExportXmlFile() val newFile = prefFileList.newExportXmlFile()
//log.debug("XXXXX " + classicPrefsFormat.UserEntriesToCsv(entries))
try { try {
classicPrefsFormat.saveCsv(newFile, entries) classicPrefsFormat.saveCsv(newFile, entries)

View file

@ -16,7 +16,6 @@ import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
@ -34,7 +33,6 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var importExportPrefs: ImportExportPrefsInterface @Inject lateinit var importExportPrefs: ImportExportPrefsInterface
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@ -70,7 +68,6 @@ class MaintenanceFragment : DaggerFragment() {
databaseHelper.resetDatabases() databaseHelper.resetDatabases()
// should be handled by Plugin-Interface and // should be handled by Plugin-Interface and
// additional service interface and plugin registry // additional service interface and plugin registry
foodPlugin.service?.resetFood()
treatmentsPlugin.service.resetTreatments() treatmentsPlugin.service.resetTreatments()
repository.clearDatabases() repository.clearDatabases()
} }

View file

@ -173,7 +173,7 @@ class MaintenancePlugin @Inject constructor(
builder.append("Build: " + BuildConfig.BUILDVERSION + System.lineSeparator()) builder.append("Build: " + BuildConfig.BUILDVERSION + System.lineSeparator())
builder.append("Remote: " + BuildConfig.REMOTE + System.lineSeparator()) builder.append("Remote: " + BuildConfig.REMOTE + System.lineSeparator())
builder.append("Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + System.lineSeparator()) builder.append("Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + System.lineSeparator())
builder.append(resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.nightscoutVersionName + System.lineSeparator()) builder.append(resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.getVersion() + System.lineSeparator())
if (buildHelper.isEngineeringMode()) builder.append(resourceHelper.gs(R.string.engineering_mode_enabled)) if (buildHelper.isEngineeringMode()) builder.append(resourceHelper.gs(R.string.engineering_mode_enabled))
return sendMail(attachmentUri, recipient, subject, builder.toString()) return sendMail(attachmentUri, recipient, subject, builder.toString())
} }

View file

@ -0,0 +1,178 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit
import info.nightscout.androidaps.database.transactions.SyncTemporaryTargetTransaction
import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction
import info.nightscout.androidaps.events.EventNsTreatment
import info.nightscout.androidaps.interfaces.ConfigInterface
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
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.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.JsonHelper.safeGetLong
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.temporaryTargetFromJson
import info.nightscout.androidaps.utils.extensions.therapyEventFromJson
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
class NSClientAddUpdateWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var sp: SP
@Inject lateinit var dateutil: DateUtil
@Inject lateinit var config: ConfigInterface
@Inject lateinit var repository: AppRepository
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@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.failure()
val treatments = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure()
var ret = Result.success()
var latestDateInReceivedData = 0L
for (i in 0 until treatments.length()) {
val json = treatments.getJSONObject(i)
// new DB model
val insulin = JsonHelper.safeGetDouble(json, "insulin")
val carbs = JsonHelper.safeGetDouble(json, "carbs")
val eventType = JsonHelper.safeGetString(json, "eventType")
if (eventType == null) {
aapsLogger.debug(LTag.DATASERVICE, "Wrong treatment. Ignoring : $json")
continue
}
//Find latest date in treatment
val mills = safeGetLong(json, "mills")
if (mills != 0L && mills < dateutil._now())
if (mills > latestDateInReceivedData) latestDateInReceivedData = mills
when {
insulin > 0 || carbs > 0 ->
rxBus.send(EventNsTreatment(EventNsTreatment.ADD, json))
eventType == TherapyEvent.Type.TEMPORARY_TARGET.text ->
temporaryTargetFromJson(json)?.let { temporaryTarget ->
repository.runTransactionForResult(SyncTemporaryTargetTransaction(temporaryTarget))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
ret = Result.failure()
}
.blockingGet()
.also { result ->
result.inserted.forEach {
uel.log(UserEntry.Action.TT_FROM_NS,
ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent),
ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true),
ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget),
ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, true)
)
}
result.invalidated.forEach {
uel.log(UserEntry.Action.TT_DELETED_FROM_NS,
ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent),
ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true),
ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget),
ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, true)
)
}
result.ended.forEach {
uel.log(UserEntry.Action.TT_CANCELED_FROM_NS,
ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent),
ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true),
ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget),
ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, true)
)
}
}
} ?: 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 ||
eventType == TherapyEvent.Type.FINGER_STICK_BG_VALUE.text ||
eventType == TherapyEvent.Type.NOTE.text ||
eventType == TherapyEvent.Type.NONE.text ||
eventType == TherapyEvent.Type.ANNOUNCEMENT.text ||
eventType == TherapyEvent.Type.QUESTION.text ||
eventType == TherapyEvent.Type.EXERCISE.text ||
eventType == TherapyEvent.Type.APS_OFFLINE.text ||
eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text ->
therapyEventFromJson(json)?.let { therapyEvent ->
repository.runTransactionForResult(SyncTherapyEventTransaction(therapyEvent))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it)
ret = Result.failure()
}
.blockingGet()
.also { result ->
result.inserted.forEach {
uel.log(UserEntry.Action.CAREPORTAL_FROM_NS,
it.note ?: "",
ValueWithUnit(it.timestamp, UserEntry.Units.Timestamp, true),
ValueWithUnit(it.type.text, UserEntry.Units.TherapyEvent)
)
}
result.invalidated.forEach {
uel.log(UserEntry.Action.CAREPORTAL_DELETED_FROM_NS,
it.note ?: "",
ValueWithUnit(it.timestamp, UserEntry.Units.Timestamp, true),
ValueWithUnit(it.type.text, UserEntry.Units.TherapyEvent)
)
}
}
} ?: aapsLogger.error("Error parsing TherapyEvent json $json")
eventType == TherapyEvent.Type.TEMPORARY_BASAL.text ->
databaseHelper.createTempBasalFromJsonIfNotExists(json)
eventType == TherapyEvent.Type.COMBO_BOLUS.text ->
databaseHelper.createExtendedBolusFromJsonIfNotExists(json)
eventType == TherapyEvent.Type.PROFILE_SWITCH.text ->
databaseHelper.createProfileSwitchFromJsonIfNotExists(json)
}
if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) {
val date = safeGetLong(json, "mills")
val now = System.currentTimeMillis()
val enteredBy = JsonHelper.safeGetString(json, "enteredBy", "")
val notes = JsonHelper.safeGetString(json, "notes", "")
if (date > now - 15 * 60 * 1000L && notes.isNotEmpty()
&& enteredBy != sp.getString("careportal_enteredby", "AndroidAPS")) {
val defaultVal = config.NSCLIENT
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
val announcement = Notification(Notification.NS_ANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60)
rxBus.send(EventNewNotification(announcement))
}
}
}
}
nsClientPlugin.updateLatestDateReceivedIfNewer(latestDateInReceivedData)
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -0,0 +1,59 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction
import info.nightscout.androidaps.interfaces.ConfigInterface
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.utils.extensions.therapyEventFromNsMbg
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
class NSClientMbgWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var repository: AppRepository
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var config: ConfigInterface
override fun doWork(): Result {
var ret = Result.success()
val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT
if (!acceptNSData) return ret
val mbgArray = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure()
for (i in 0 until mbgArray.length()) {
val nsMbg = NSMbg(mbgArray.getJSONObject(i))
if (!nsMbg.isValid()) continue
repository.runTransactionForResult(SyncTherapyEventTransaction(therapyEventFromNsMbg(nsMbg)))
.doOnError {
aapsLogger.error("Error while saving therapy event", it)
ret = Result.failure()
}
.blockingGet()
.also {
aapsLogger.debug(LTag.DATABASE, "Saved therapy event $it")
}
}
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -31,7 +31,9 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.database.AppRepository; import info.nightscout.androidaps.database.AppRepository;
import info.nightscout.androidaps.database.entities.TemporaryTarget; import info.nightscout.androidaps.database.entities.TemporaryTarget;
import info.nightscout.androidaps.database.entities.TherapyEvent; import info.nightscout.androidaps.database.entities.TherapyEvent;
import info.nightscout.androidaps.database.entities.UserEntry.*; import info.nightscout.androidaps.database.entities.UserEntry.Action;
import info.nightscout.androidaps.database.entities.UserEntry.Units;
import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit;
import info.nightscout.androidaps.database.transactions.SyncTemporaryTargetTransaction; import info.nightscout.androidaps.database.transactions.SyncTemporaryTargetTransaction;
import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction; import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction;
import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventAppExit;
@ -50,7 +52,6 @@ import info.nightscout.androidaps.logging.UserEntryLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck; 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.NSAlarm;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog; 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.EventNSClientResend;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus;
@ -73,7 +74,6 @@ import static info.nightscout.androidaps.utils.extensions.TemporaryTargetExtensi
import static info.nightscout.androidaps.utils.extensions.TemporaryTargetExtensionKt.temporaryTargetFromNsIdForInvalidating; import static info.nightscout.androidaps.utils.extensions.TemporaryTargetExtensionKt.temporaryTargetFromNsIdForInvalidating;
import static info.nightscout.androidaps.utils.extensions.TherapyEventExtensionKt.therapyEventFromJson; import static info.nightscout.androidaps.utils.extensions.TherapyEventExtensionKt.therapyEventFromJson;
import static info.nightscout.androidaps.utils.extensions.TherapyEventExtensionKt.therapyEventFromNsIdForInvalidating; import static info.nightscout.androidaps.utils.extensions.TherapyEventExtensionKt.therapyEventFromNsIdForInvalidating;
import static info.nightscout.androidaps.utils.extensions.TherapyEventExtensionKt.therapyEventFromNsMbg;
@Singleton @Singleton
public class NSClientPlugin extends PluginBase { public class NSClientPlugin extends PluginBase {
@ -257,7 +257,7 @@ public class NSClientPlugin extends PluginBase {
SwitchPreference key_ns_sync_use_absolute = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_sync_use_absolute)); 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); if (key_ns_sync_use_absolute != null) key_ns_sync_use_absolute.setVisible(false);
} else { } else {
// APS or pumpcontrol mode // APS or pumpControl mode
SwitchPreference key_ns_upload_only = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_upload_only)); SwitchPreference key_ns_upload_only = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_upload_only));
if (key_ns_upload_only != null) if (key_ns_upload_only != null)
key_ns_upload_only.setVisible(buildHelper.isEngineeringMode()); key_ns_upload_only.setVisible(buildHelper.isEngineeringMode());
@ -353,178 +353,8 @@ public class NSClientPlugin extends PluginBase {
nsClientService.sendAlarmAck(ack); nsClientService.sendAlarmAck(ack);
} }
// Parsing input data public void updateLatestDateReceivedIfNewer(long latestReceived) {
if (latestReceived > nsClientService.latestDateInReceivedData)
public void handleNewDataFromNSClient(String action, Bundle bundle) { nsClientService.latestDateInReceivedData = latestReceived;
boolean acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.getNSCLIENT();
if (!acceptNSData) return;
aapsLogger.debug(LTag.DATASERVICE, "Got intent: " + action);
if (action.equals(Intents.ACTION_NEW_TREATMENT) || action.equals(Intents.ACTION_CHANGED_TREATMENT)) {
try {
if (bundle.containsKey("treatment")) {
JSONObject json = new JSONObject(bundle.getString("treatment"));
handleTreatmentFromNS(json, action);
}
if (bundle.containsKey("treatments")) {
String trstring = bundle.getString("treatments");
JSONArray jsonArray = new JSONArray(trstring);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject json = jsonArray.getJSONObject(i);
handleTreatmentFromNS(json, action);
}
}
} catch (JSONException e) {
aapsLogger.error(LTag.DATASERVICE, "Unhandled exception", e);
}
}
if (action.equals(Intents.ACTION_REMOVED_TREATMENT)) {
try {
if (bundle.containsKey("treatment")) {
String trstring = bundle.getString("treatment");
JSONObject json = new JSONObject(trstring);
handleRemovedTreatmentFromNS(json);
}
if (bundle.containsKey("treatments")) {
String trstring = bundle.getString("treatments");
JSONArray jsonArray = new JSONArray(trstring);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject json = jsonArray.getJSONObject(i);
handleRemovedTreatmentFromNS(json);
}
}
} catch (JSONException e) {
aapsLogger.error(LTag.DATASERVICE, "Unhandled exception", e);
}
}
if (action.equals(Intents.ACTION_NEW_MBG)) {
try {
if (bundle.containsKey("mbg")) {
String mbgstring = bundle.getString("mbg");
JSONObject mbgJson = new JSONObject(mbgstring);
storeMbg(mbgJson);
}
if (bundle.containsKey("mbgs")) {
String sgvstring = bundle.getString("mbgs");
JSONArray jsonArray = new JSONArray(sgvstring);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject mbgJson = jsonArray.getJSONObject(i);
storeMbg(mbgJson);
}
}
} catch (Exception e) {
aapsLogger.error(LTag.DATASERVICE, "Unhandled exception", e);
}
}
}
private void handleRemovedTreatmentFromNS(JSONObject json) {
String _id = JsonHelper.safeGetString(json, "_id");
if (_id == null) return;
// room Temporary target
TemporaryTarget temporaryTarget = temporaryTargetFromNsIdForInvalidating(_id);
disposable.add(repository.runTransactionForResult(new SyncTemporaryTargetTransaction(temporaryTarget)).subscribe(
result -> result.getInvalidated().forEach(record -> uel.log(Action.TT_DELETED_FROM_NS, new ValueWithUnit(record.getReason().getText(), Units.TherapyEvent), new ValueWithUnit(record.getLowTarget(), Units.Mg_Dl, true), new ValueWithUnit(record.getHighTarget(), Units.Mg_Dl, record.getLowTarget() != record.getHighTarget()), new ValueWithUnit((int) record.getDuration()/60000, Units.M, record.getDuration() != 0))),
error -> aapsLogger.error(LTag.DATABASE, "Error while removing temporary target", error)));
// room Therapy Event
TherapyEvent therapyEvent = therapyEventFromNsIdForInvalidating(_id);
disposable.add(repository.runTransactionForResult(new SyncTherapyEventTransaction(therapyEvent)).subscribe(
result -> result.getInvalidated().forEach(record -> uel.log(Action.CAREPORTAL_DELETED_FROM_NS, record.getNote() , new ValueWithUnit(record.getTimestamp(), Units.Timestamp, true), new ValueWithUnit(record.getType().getText(), Units.TherapyEvent))),
error -> aapsLogger.error(LTag.DATABASE, "Error while removing therapy event", error)));
// new DB model
EventNsTreatment evtTreatment = new EventNsTreatment(EventNsTreatment.Companion.getREMOVE(), json);
rxBus.send(evtTreatment);
// old DB model
databaseHelper.deleteTempBasalById(_id);
databaseHelper.deleteExtendedBolusById(_id);
databaseHelper.deleteProfileSwitchById(_id);
}
private void handleTreatmentFromNS(JSONObject json, String action) {
// new DB model
int mode = Intents.ACTION_NEW_TREATMENT.equals(action) ? EventNsTreatment.Companion.getADD() : EventNsTreatment.Companion.getUPDATE();
double insulin = JsonHelper.safeGetDouble(json, "insulin");
double carbs = JsonHelper.safeGetDouble(json, "carbs");
String eventType = JsonHelper.safeGetString(json, "eventType");
if (eventType == null) {
aapsLogger.debug(LTag.DATASERVICE, "Wrong treatment. Ignoring : " + json.toString());
return;
}
if (insulin > 0 || carbs > 0) {
EventNsTreatment evtTreatment = new EventNsTreatment(mode, json);
rxBus.send(evtTreatment);
} else if (eventType.equals(TherapyEvent.Type.TEMPORARY_TARGET.getText())) {
TemporaryTarget temporaryTarget = temporaryTargetFromJson(json);
if (temporaryTarget != null) {
disposable.add(repository.runTransactionForResult(new SyncTemporaryTargetTransaction(temporaryTarget))
.subscribe(
result -> {
result.getInserted().forEach(record -> uel.log(Action.TT_FROM_NS, new ValueWithUnit(record.getReason().getText(), Units.TherapyEvent), new ValueWithUnit(record.getLowTarget(), Units.Mg_Dl, true), new ValueWithUnit(record.getHighTarget(), Units.Mg_Dl, record.getLowTarget() != record.getHighTarget()), new ValueWithUnit((int) record.getDuration()/60000, Units.M, true)));
result.getInvalidated().forEach(record -> uel.log(Action.TT_DELETED_FROM_NS, new ValueWithUnit(record.getReason().getText(), Units.TherapyEvent), new ValueWithUnit(record.getLowTarget(), Units.Mg_Dl, true), new ValueWithUnit(record.getHighTarget(), Units.Mg_Dl, record.getLowTarget() != record.getHighTarget()), new ValueWithUnit((int) record.getDuration()/60000, Units.M, true)));
result.getEnded().forEach(record -> uel.log(Action.TT_CANCELED_FROM_NS, new ValueWithUnit(record.getReason().getText(), Units.TherapyEvent), new ValueWithUnit(record.getLowTarget(), Units.Mg_Dl, true), new ValueWithUnit(record.getHighTarget(), Units.Mg_Dl, record.getLowTarget() != record.getHighTarget()), new ValueWithUnit((int) record.getDuration()/60000, Units.M, true)));
},
error -> aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", error)));
} else {
aapsLogger.error("Error parsing TT json " + json.toString());
}
} else if (eventType.equals(TherapyEvent.Type.TEMPORARY_BASAL.getText())) {
databaseHelper.createTempBasalFromJsonIfNotExists(json);
} else if (eventType.equals(TherapyEvent.Type.COMBO_BOLUS.getText())) {
databaseHelper.createExtendedBolusFromJsonIfNotExists(json);
} else if (eventType.equals(TherapyEvent.Type.PROFILE_SWITCH.getText())) {
databaseHelper.createProfileSwitchFromJsonIfNotExists(activePlugin, nsUpload, json);
} else if (eventType.equals(TherapyEvent.Type.CANNULA_CHANGE.getText()) ||
eventType.equals(TherapyEvent.Type.INSULIN_CHANGE.getText()) ||
eventType.equals(TherapyEvent.Type.SENSOR_CHANGE.getText()) ||
eventType.equals(TherapyEvent.Type.FINGER_STICK_BG_VALUE.getText()) ||
eventType.equals(TherapyEvent.Type.NOTE.getText()) ||
eventType.equals(TherapyEvent.Type.NONE.getText()) ||
eventType.equals(TherapyEvent.Type.ANNOUNCEMENT.getText()) ||
eventType.equals(TherapyEvent.Type.QUESTION.getText()) ||
eventType.equals(TherapyEvent.Type.EXERCISE.getText()) ||
eventType.equals(TherapyEvent.Type.APS_OFFLINE.getText()) ||
eventType.equals(TherapyEvent.Type.PUMP_BATTERY_CHANGE.getText())) {
TherapyEvent therapyEvent = therapyEventFromJson(json);
if (therapyEvent != null) {
disposable.add(repository.runTransactionForResult(new SyncTherapyEventTransaction(therapyEvent))
.subscribe(
result -> {
result.getInserted().forEach(record -> uel.log(Action.CAREPORTAL_FROM_NS, record.getNote() , new ValueWithUnit(record.getTimestamp(), Units.Timestamp, true), new ValueWithUnit(record.getType().getText(), Units.TherapyEvent)));
result.getInvalidated().forEach(record -> uel.log(Action.CAREPORTAL_DELETED_FROM_NS, record.getNote() , new ValueWithUnit(record.getTimestamp(), Units.Timestamp, true), new ValueWithUnit(record.getType().getText(), Units.TherapyEvent)));
},
error -> aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", error)));
} else {
aapsLogger.error("Error parsing TherapyEvent json " + json.toString());
}
}
if (eventType.equals(TherapyEvent.Type.ANNOUNCEMENT.getText())) {
long date = JsonHelper.safeGetLong(json, "mills");
long now = System.currentTimeMillis();
String enteredBy = JsonHelper.safeGetString(json, "enteredBy", "");
String notes = JsonHelper.safeGetString(json, "notes", "");
if (date > now - 15 * 60 * 1000L && !notes.isEmpty()
&& !enteredBy.equals(sp.getString("careportal_enteredby", "AndroidAPS"))) {
boolean defaultVal = config.getNSCLIENT();
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
Notification announcement = new Notification(Notification.NS_ANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60);
rxBus.send(new EventNewNotification(announcement));
}
}
}
}
private void storeMbg(JSONObject mbgJson) {
NSMbg nsMbg = new NSMbg(getInjector(), mbgJson);
if (nsMbg.mbg != 0.0 && nsMbg.date != 0)
disposable.add(repository.runTransactionForResult(new SyncTherapyEventTransaction(therapyEventFromNsMbg(nsMbg)))
.subscribe(
result -> aapsLogger.debug(LTag.DATABASE, "Saved therapy event" + result),
error -> aapsLogger.error("Error while saving therapy event", error))
);
} }
} }

View file

@ -0,0 +1,111 @@
package info.nightscout.androidaps.plugins.general.nsclient
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit
import info.nightscout.androidaps.database.transactions.SyncTemporaryTargetTransaction
import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction
import info.nightscout.androidaps.events.EventNsTreatment
import info.nightscout.androidaps.events.EventNsTreatment.Companion.REMOVE
import info.nightscout.androidaps.interfaces.ConfigInterface
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
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.receivers.DataWorker
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.temporaryTargetFromNsIdForInvalidating
import info.nightscout.androidaps.utils.extensions.therapyEventFromNsIdForInvalidating
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
// This will not be needed fpr NS v3
// Now NS provides on _id of removed records
class NSClientRemoveWorker(
context: Context,
params: WorkerParameters) : Worker(context, params) {
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var sp: SP
@Inject lateinit var config: ConfigInterface
@Inject lateinit var repository: AppRepository
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@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.failure()
var ret = Result.success()
val treatments = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure()
for (i in 0 until treatments.length()) {
val json = treatments.getJSONObject(i)
val nsId = JsonHelper.safeGetString(json, "_id") ?: continue
// room Temporary target
val temporaryTarget = temporaryTargetFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncTemporaryTargetTransaction(temporaryTarget))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while removing temporary target", it)
ret = Result.failure()
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(
UserEntry.Action.TT_DELETED_FROM_NS,
ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent),
ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true),
ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget),
ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, it.duration != 0L)
)
}
}
// room Therapy Event
val therapyEvent = therapyEventFromNsIdForInvalidating(nsId)
repository.runTransactionForResult(SyncTherapyEventTransaction(therapyEvent))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while removing therapy event", it)
ret = Result.failure()
}
.blockingGet()
.also { result ->
result.invalidated.forEach {
uel.log(
UserEntry.Action.CAREPORTAL_DELETED_FROM_NS, (it.note ?: ""),
ValueWithUnit(it.timestamp, UserEntry.Units.Timestamp, true),
ValueWithUnit(it.type.text, UserEntry.Units.TherapyEvent))
}
}
// Insulin, carbs
rxBus.send(EventNsTreatment(REMOVE, json))
// old DB model
databaseHelper.deleteTempBasalById(nsId)
databaseHelper.deleteExtendedBolusById(nsId)
databaseHelper.deleteProfileSwitchById(nsId)
}
return ret
}
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
}

View file

@ -1,41 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import androidx.annotation.NonNull;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.receivers.BundleStore;
import info.nightscout.androidaps.receivers.DataReceiver;
// cannot be inner class because of needed injection
public class NSClientWorker extends Worker {
public NSClientWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
((HasAndroidInjector) context.getApplicationContext()).androidInjector().inject(this);
}
@Inject NSClientPlugin nsClientPlugin;
@Inject BundleStore bundleStore;
@NonNull
@Override
public Result doWork() {
Bundle bundle = bundleStore.pickup(getInputData().getLong(DataReceiver.STORE_KEY, -1));
if (bundle == null) return Result.failure();
String action = getInputData().getString(DataReceiver.ACTION_KEY);
nsClientPlugin.handleNewDataFromNSClient(action, bundle);
return Result.success();
}
}

View file

@ -1,481 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient.data;
import android.text.Spanned;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.ConfigInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.HtmlHelper;
import info.nightscout.androidaps.utils.Round;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
/**
* Created by mike on 25.06.2017.
*/
/*
{
"_id": "594fdcec327b83c81b6b8c0f",
"device": "openaps://Sony D5803",
"pump": {
"battery": {
"percent": 100
},
"status": {
"status": "normal",
"timestamp": "2017-06-25T15:50:14Z"
},
"extended": {
"Version": "1.5-ac98852-2017.06.25",
"PumpIOB": 1.13,
"LastBolus": "25. 6. 2017 17:25:00",
"LastBolusAmount": 0.3,
"BaseBasalRate": 0.4,
"ActiveProfile": "2016 +30%"
},
"reservoir": 109,
"clock": "2017-06-25T15:55:10Z"
},
"openaps": {
"suggested": {
"temp": "absolute",
"bg": 115.9,
"tick": "+5",
"eventualBG": 105,
"snoozeBG": 105,
"predBGs": {
"IOB": [116, 114, 112, 110, 109, 107, 106, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 107]
},
"COB": 0,
"IOB": -0.035,
"reason": "COB: 0, Dev: -18, BGI: 0.43, ISF: 216, Target: 99; Eventual BG 105 > 99 but Min. Delta -2.60 < Exp. Delta 0.1; setting current basal of 0.4 as temp. Suggested rate is same as profile rate, no temp basal is active, doing nothing",
"timestamp": "2017-06-25T15:55:10Z"
},
"iob": {
"iob": -0.035,
"basaliob": -0.035,
"activity": -0.0004,
"time": "2017-06-25T15:55:10Z"
}
},
"uploaderBattery": 93,
"created_at": "2017-06-25T15:55:10Z",
"NSCLIENT_ID": 1498406118857
}
*/
@Singleton
public class NSDeviceStatus {
private final AAPSLogger aapsLogger;
private final SP sp;
private final ResourceHelper resourceHelper;
private final NSSettingsStatus nsSettingsStatus;
private final ConfigInterface config;
private final RunningConfiguration runningConfiguration;
private JSONObject data = null;
@Inject
public NSDeviceStatus(
AAPSLogger aapsLogger,
SP sp,
ResourceHelper resourceHelper,
NSSettingsStatus nsSettingsStatus,
ConfigInterface config,
RunningConfiguration runningConfiguration
) {
this.aapsLogger = aapsLogger;
this.sp = sp;
this.resourceHelper = resourceHelper;
this.nsSettingsStatus = nsSettingsStatus;
this.config = config;
this.runningConfiguration = runningConfiguration;
}
public void handleNewData(JSONArray devicestatuses) {
aapsLogger.debug(LTag.NSCLIENT, "Got NS devicestatus: $devicestatuses}");
for (int i = 0; i < devicestatuses.length(); i++) {
try {
JSONObject devicestatusJson = devicestatuses.getJSONObject(i);
if (devicestatusJson != null) {
setData(devicestatusJson);
if (devicestatusJson.has("pump")) {
// Objectives 0
sp.putBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, true);
}
if (devicestatusJson.has("configuration") && config.getNSCLIENT()) {
// copy configuration of Insulin and Sensitivity from main AAPS
runningConfiguration.apply(devicestatusJson.getJSONObject("configuration"));
}
}
} catch (JSONException jsonException) {
jsonException.printStackTrace();
}
}
}
public NSDeviceStatus setData(JSONObject obj) {
this.data = obj;
updatePumpData();
updateOpenApsData(obj);
updateUploaderData(obj);
return this;
}
public String getDevice() {
try {
if (data.has("device")) {
String device = data.getString("device");
if (device.startsWith("openaps://")) {
device = device.substring(10);
return device;
}
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
return "";
}
public static class Levels {
static int URGENT = 2;
static int WARN = 1;
static int INFO = 0;
int LOW = -1;
int LOWEST = -2;
static int NONE = -3;
}
// ***** PUMP DATA ******
private DeviceStatusPumpData deviceStatusPumpData = null;
public Spanned getExtendedPumpStatus() {
if (deviceStatusPumpData != null && deviceStatusPumpData.extended != null)
return deviceStatusPumpData.extended;
return HtmlHelper.INSTANCE.fromHtml("");
}
public Spanned getPumpStatus() {
//String[] ALL_STATUS_FIELDS = {"reservoir", "battery", "clock", "status", "device"};
StringBuilder string = new StringBuilder();
string.append("<span style=\"color:" + resourceHelper.gcs(R.color.defaulttext) + "\">");
string.append(resourceHelper.gs(R.string.pump));
string.append(": </span>");
if (deviceStatusPumpData == null)
return HtmlHelper.INSTANCE.fromHtml("");
// test warning level
int level = Levels.INFO;
long now = System.currentTimeMillis();
if (deviceStatusPumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < now)
level = Levels.URGENT;
else if (deviceStatusPumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes"))
level = Levels.URGENT;
else if (deviceStatusPumpData.isPercent && deviceStatusPumpData.percent < nsSettingsStatus.extendedPumpSettings("urgentBattP"))
level = Levels.URGENT;
else if (!deviceStatusPumpData.isPercent && deviceStatusPumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV"))
level = Levels.URGENT;
else if (deviceStatusPumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < now)
level = Levels.WARN;
else if (deviceStatusPumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes"))
level = Levels.WARN;
else if (deviceStatusPumpData.isPercent && deviceStatusPumpData.percent < nsSettingsStatus.extendedPumpSettings("warnBattP"))
level = Levels.WARN;
else if (!deviceStatusPumpData.isPercent && deviceStatusPumpData.voltage < nsSettingsStatus.extendedPumpSettings("warnBattV"))
level = Levels.WARN;
string.append("<span style=\"color:");
if (level == Levels.INFO) string.append("white\">");
if (level == Levels.WARN) string.append("yellow\">");
if (level == Levels.URGENT) string.append("red\">");
String fields = nsSettingsStatus.pumpExtendedSettingsFields();
if (fields.contains("reservoir")) {
string.append((int) deviceStatusPumpData.reservoir).append("U ");
}
if (fields.contains("battery") && deviceStatusPumpData.isPercent) {
string.append(deviceStatusPumpData.percent).append("% ");
}
if (fields.contains("battery") && !deviceStatusPumpData.isPercent) {
string.append(Round.roundTo(deviceStatusPumpData.voltage, 0.001d)).append(" ");
}
if (fields.contains("clock")) {
string.append(DateUtil.minAgo(resourceHelper, deviceStatusPumpData.clock)).append(" ");
}
if (fields.contains("status")) {
string.append(deviceStatusPumpData.status).append(" ");
}
if (fields.contains("device")) {
string.append(getDevice()).append(" ");
}
string.append("</span>"); // color
return HtmlHelper.INSTANCE.fromHtml(string.toString());
}
static class DeviceStatusPumpData {
long clock = 0L;
boolean isPercent = false;
int percent = 0;
double voltage = 0;
String status = "N/A";
double reservoir = 0d;
Spanned extended = null;
}
private void updatePumpData() {
try {
JSONObject pump = data != null && data.has("pump") ? data.getJSONObject("pump") : new JSONObject();
long clock = 0L;
if (pump.has("clock"))
clock = DateUtil.fromISODateString(pump.getString("clock")).getTime();
// check if this is new data
if (clock == 0 || deviceStatusPumpData != null && clock < deviceStatusPumpData.clock)
return;
// create new status and process data
deviceStatusPumpData = new DeviceStatusPumpData();
deviceStatusPumpData.clock = clock;
if (pump.has("status") && pump.getJSONObject("status").has("status"))
deviceStatusPumpData.status = pump.getJSONObject("status").getString("status");
if (pump.has("reservoir"))
deviceStatusPumpData.reservoir = pump.getDouble("reservoir");
if (pump.has("battery") && pump.getJSONObject("battery").has("percent")) {
deviceStatusPumpData.isPercent = true;
deviceStatusPumpData.percent = pump.getJSONObject("battery").getInt("percent");
} else if (pump.has("battery") && pump.getJSONObject("battery").has("voltage")) {
deviceStatusPumpData.isPercent = false;
deviceStatusPumpData.voltage = pump.getJSONObject("battery").getDouble("voltage");
}
if (pump.has("extended")) {
JSONObject extendedJson = pump.getJSONObject("extended");
StringBuilder exteneded = new StringBuilder();
Iterator<?> keys = extendedJson.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
String value = extendedJson.getString(key);
exteneded.append("<b>").append(key).append(":</b> ").append(value).append("<br>");
}
deviceStatusPumpData.extended = HtmlHelper.INSTANCE.fromHtml(exteneded.toString());
}
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
}
// ********* OpenAPS data ***********
public static DeviceStatusOpenAPSData deviceStatusOpenAPSData = new DeviceStatusOpenAPSData();
public static class DeviceStatusOpenAPSData {
public long clockSuggested = 0L;
public long clockEnacted = 0L;
public JSONObject suggested = null;
public JSONObject enacted = null;
}
private void updateOpenApsData(JSONObject object) {
try {
JSONObject openaps = object.has("openaps") ? object.getJSONObject("openaps") : new JSONObject();
JSONObject suggested = openaps.has("suggested") ? openaps.getJSONObject("suggested") : new JSONObject();
JSONObject enacted = openaps.has("enacted") ? openaps.getJSONObject("enacted") : new JSONObject();
long clock = 0L;
if (suggested.has("timestamp"))
clock = DateUtil.fromISODateString(suggested.getString("timestamp")).getTime();
// check if this is new data
if (clock != 0 && clock > deviceStatusOpenAPSData.clockSuggested) {
deviceStatusOpenAPSData.suggested = suggested;
deviceStatusOpenAPSData.clockSuggested = clock;
}
clock = 0L;
if (enacted.has("timestamp"))
clock = DateUtil.fromISODateString(enacted.getString("timestamp")).getTime();
// check if this is new data
if (clock != 0 && clock > deviceStatusOpenAPSData.clockEnacted) {
deviceStatusOpenAPSData.enacted = enacted;
deviceStatusOpenAPSData.clockEnacted = clock;
}
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
}
public Spanned getOpenApsStatus() {
StringBuilder string = new StringBuilder();
string.append("<span style=\"color:" + resourceHelper.gcs(R.color.defaulttext) + "\">");
string.append(resourceHelper.gs(R.string.openaps_short));
string.append(": </span>");
// test warning level
int level = Levels.INFO;
long now = System.currentTimeMillis();
if (deviceStatusOpenAPSData.clockSuggested != 0 && deviceStatusOpenAPSData.clockSuggested + sp.getInt(R.string.key_nsalarm_urgent_staledatavalue, 31) * 60 * 1000L < now)
level = Levels.URGENT;
else if (deviceStatusOpenAPSData.clockSuggested != 0 && deviceStatusOpenAPSData.clockSuggested + sp.getInt(R.string.key_nsalarm_staledatavalue, 16) * 60 * 1000L < now)
level = Levels.WARN;
string.append("<span style=\"color:");
if (level == Levels.INFO) string.append("white\">");
if (level == Levels.WARN) string.append("yellow\">");
if (level == Levels.URGENT) string.append("red\">");
if (deviceStatusOpenAPSData.clockSuggested != 0) {
string.append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append(" ");
}
string.append("</span>"); // color
return HtmlHelper.INSTANCE.fromHtml(string.toString());
}
public static long getOpenApsTimestamp() {
if (deviceStatusOpenAPSData.clockSuggested != 0) {
return deviceStatusOpenAPSData.clockSuggested;
} else {
return -1;
}
}
public Spanned getExtendedOpenApsStatus() {
StringBuilder string = new StringBuilder();
try {
if (deviceStatusOpenAPSData.enacted != null && deviceStatusOpenAPSData.clockEnacted != deviceStatusOpenAPSData.clockSuggested)
string.append("<b>").append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockEnacted)).append("</b> ").append(deviceStatusOpenAPSData.enacted.getString("reason")).append("<br>");
if (deviceStatusOpenAPSData.suggested != null)
string.append("<b>").append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append("</b> ").append(deviceStatusOpenAPSData.suggested.getString("reason")).append("<br>");
return HtmlHelper.INSTANCE.fromHtml(string.toString());
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
return HtmlHelper.INSTANCE.fromHtml("");
}
// ********* Uploader data ***********
private static final HashMap<String, Uploader> uploaders = new HashMap<>();
static class Uploader {
long clock = 0L;
int battery = 0;
}
private void updateUploaderData(JSONObject object) {
try {
long clock = 0L;
if (object.has("mills"))
clock = object.getLong("mills");
else if (object.has("created_at"))
clock = DateUtil.fromISODateString(object.getString("created_at")).getTime();
String device = getDevice();
Integer battery = null;
if (object.has("uploaderBattery"))
battery = object.getInt("uploaderBattery");
else if (object.has("uploader")) {
if (object.getJSONObject("uploader").has("battery"))
battery = object.getJSONObject("uploader").getInt("battery");
}
Uploader uploader = uploaders.get(device);
// check if this is new data
if (clock != 0 && battery != null && (uploader == null || clock > uploader.clock)) {
if (uploader == null)
uploader = new Uploader();
uploader.battery = battery;
uploader.clock = clock;
uploaders.put(device, uploader);
}
} catch (Exception e) {
aapsLogger.error("Unhandled exception", e);
}
}
public String getUploaderStatus() {
Iterator iter = uploaders.entrySet().iterator();
int minBattery = 100;
while (iter.hasNext()) {
Map.Entry pair = (Map.Entry) iter.next();
Uploader uploader = (Uploader) pair.getValue();
if (minBattery > uploader.battery)
minBattery = uploader.battery;
}
return minBattery + "%";
}
public Spanned getUploaderStatusSpanned() {
StringBuilder string = new StringBuilder();
string.append("<span style=\"color:" + resourceHelper.gcs(R.color.defaulttext) + "\">");
string.append(resourceHelper.gs(R.string.uploader_short));
string.append(": </span>");
Iterator iter = uploaders.entrySet().iterator();
int minBattery = 100;
while (iter.hasNext()) {
Map.Entry pair = (Map.Entry) iter.next();
Uploader uploader = (Uploader) pair.getValue();
if (minBattery > uploader.battery)
minBattery = uploader.battery;
}
string.append(minBattery);
string.append("%");
return HtmlHelper.INSTANCE.fromHtml(string.toString());
}
public Spanned getExtendedUploaderStatus() {
StringBuilder string = new StringBuilder();
Iterator iter = uploaders.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry pair = (Map.Entry) iter.next();
Uploader uploader = (Uploader) pair.getValue();
String device = (String) pair.getKey();
string.append("<b>").append(device).append(":</b> ").append(uploader.battery).append("%<br>");
}
return HtmlHelper.INSTANCE.fromHtml(string.toString());
}
public static APSResult getAPSResult(HasAndroidInjector injector) {
APSResult result = new APSResult(injector);
result.setJson(deviceStatusOpenAPSData.suggested);
result.setDate(deviceStatusOpenAPSData.clockSuggested);
return result;
}
}

View file

@ -0,0 +1,396 @@
package info.nightscout.androidaps.plugins.general.nsclient.data
import android.text.Spanned
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ConfigInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.APSResult
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HtmlHelper.fromHtml
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
/*
{
"_id": "594fdcec327b83c81b6b8c0f",
"device": "openaps://Sony D5803",
"pump": {
"battery": {
"percent": 100
},
"status": {
"status": "normal",
"timestamp": "2017-06-25T15:50:14Z"
},
"extended": {
"Version": "1.5-ac98852-2017.06.25",
"PumpIOB": 1.13,
"LastBolus": "25. 6. 2017 17:25:00",
"LastBolusAmount": 0.3,
"BaseBasalRate": 0.4,
"ActiveProfile": "2016 +30%"
},
"reservoir": 109,
"clock": "2017-06-25T15:55:10Z"
},
"openaps": {
"suggested": {
"temp": "absolute",
"bg": 115.9,
"tick": "+5",
"eventualBG": 105,
"snoozeBG": 105,
"predBGs": {
"IOB": [116, 114, 112, 110, 109, 107, 106, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 107]
},
"COB": 0,
"IOB": -0.035,
"reason": "COB: 0, Dev: -18, BGI: 0.43, ISF: 216, Target: 99; Eventual BG 105 > 99 but Min. Delta -2.60 < Exp. Delta 0.1; setting current basal of 0.4 as temp. Suggested rate is same as profile rate, no temp basal is active, doing nothing",
"timestamp": "2017-06-25T15:55:10Z"
},
"iob": {
"iob": -0.035,
"basaliob": -0.035,
"activity": -0.0004,
"time": "2017-06-25T15:55:10Z"
}
},
"uploaderBattery": 93,
"created_at": "2017-06-25T15:55:10Z",
"NSCLIENT_ID": 1498406118857
}
*/
@Suppress("SpellCheckingInspection")
@Singleton
class NSDeviceStatus @Inject constructor(
private val aapsLogger: AAPSLogger,
private val sp: SP,
private val resourceHelper: ResourceHelper,
private val nsSettingsStatus: NSSettingsStatus,
private val config: ConfigInterface,
private val dateUtil: DateUtil,
private val runningConfiguration: RunningConfiguration
) {
private var data: JSONObject? = null
fun handleNewData(deviceStatuses: JSONArray) {
aapsLogger.debug(LTag.NSCLIENT, "Got NS deviceStatus: \$deviceStatuses}")
try {
for (i in 0 until deviceStatuses.length()) {
val devicestatusJson = deviceStatuses.getJSONObject(i)
if (devicestatusJson != null) {
setData(devicestatusJson)
if (devicestatusJson.has("pump")) {
// Objectives 0
sp.putBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, true)
}
if (devicestatusJson.has("configuration") && config.NSCLIENT) {
// copy configuration of Insulin and Sensitivity from main AAPS
runningConfiguration.apply(devicestatusJson.getJSONObject("configuration"))
}
}
}
} catch (jsonException: JSONException) {
jsonException.printStackTrace()
}
}
private fun setData(obj: JSONObject): NSDeviceStatus {
data = obj
updatePumpData()
updateOpenApsData(obj)
updateUploaderData(obj)
return this
}
val device: String
get() {
try {
if (data!!.has("device")) {
var device = data!!.getString("device")
if (device.startsWith("openaps://")) {
device = device.substring(10)
return device
}
}
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
return ""
}
enum class Levels(val level: Int) {
URGENT(2),
WARN(1),
INFO(0);
fun toColor(): String =
when (level) {
INFO.level -> "white"
WARN.level -> "yellow"
URGENT.level -> "red"
else -> "white"
}
}
// ***** PUMP DATA ******
private var deviceStatusPumpData: DeviceStatusPumpData? = null
val extendedPumpStatus: Spanned
get() = deviceStatusPumpData?.extended ?: fromHtml("")
val pumpStatus: Spanned
// test warning level // color
get() {
val pumpData = deviceStatusPumpData ?: return fromHtml("")
//String[] ALL_STATUS_FIELDS = {"reservoir", "battery", "clock", "status", "device"};
val string = StringBuilder()
.append("<span style=\"color:${resourceHelper.gcs(R.color.defaulttext)}\">")
.append(resourceHelper.gs(R.string.pump))
.append(": </span>")
// test warning level
val level = when {
pumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < dateUtil._now() -> Levels.URGENT
pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes") -> Levels.URGENT
pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("urgentBattP") -> Levels.URGENT
!pumpData.isPercent && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV") -> Levels.URGENT
pumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < dateUtil._now() -> Levels.WARN
pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes") -> Levels.WARN
pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("warnBattP") -> Levels.WARN
!pumpData.isPercent && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("warnBattV") -> Levels.WARN
else -> Levels.INFO
}
string.append("<span style=\"color:${level.toColor()}\">")
val fields = nsSettingsStatus.pumpExtendedSettingsFields()
if (fields.contains("reservoir")) string.append(pumpData.reservoir.toInt()).append("U ")
if (fields.contains("battery") && pumpData.isPercent) string.append(pumpData.percent).append("% ")
if (fields.contains("battery") && !pumpData.isPercent) string.append(Round.roundTo(pumpData.voltage, 0.001)).append(" ")
if (fields.contains("clock")) string.append(DateUtil.minAgo(resourceHelper, pumpData.clock)).append(" ")
if (fields.contains("status")) string.append(pumpData.status).append(" ")
if (fields.contains("device")) string.append(device).append(" ")
string.append("</span>") // color
return fromHtml(string.toString())
}
internal class DeviceStatusPumpData {
var clock = 0L
var isPercent = false
var percent = 0
var voltage = 0.0
var status = "N/A"
var reservoir = 0.0
var extended: Spanned? = null
}
private fun updatePumpData() {
try {
val data = this.data ?: return
val pump = if (data.has("pump")) data.getJSONObject("pump") else JSONObject()
val clock = if (pump.has("clock")) DateUtil.fromISODateString(pump.getString("clock")).time else 0L
// check if this is new data
if (clock == 0L || deviceStatusPumpData != null && clock < deviceStatusPumpData!!.clock) return
// create new status and process data
val deviceStatusPumpData = DeviceStatusPumpData()
deviceStatusPumpData.clock = clock
if (pump.has("status") && pump.getJSONObject("status").has("status")) deviceStatusPumpData.status = pump.getJSONObject("status").getString("status")
if (pump.has("reservoir")) deviceStatusPumpData.reservoir = pump.getDouble("reservoir")
if (pump.has("battery") && pump.getJSONObject("battery").has("percent")) {
deviceStatusPumpData.isPercent = true
deviceStatusPumpData.percent = pump.getJSONObject("battery").getInt("percent")
} else if (pump.has("battery") && pump.getJSONObject("battery").has("voltage")) {
deviceStatusPumpData.isPercent = false
deviceStatusPumpData.voltage = pump.getJSONObject("battery").getDouble("voltage")
}
if (pump.has("extended")) {
val extendedJson = pump.getJSONObject("extended")
val extended = StringBuilder()
val keys: Iterator<*> = extendedJson.keys()
while (keys.hasNext()) {
val key = keys.next() as String
val value = extendedJson.getString(key)
extended.append("<b>").append(key).append(":</b> ").append(value).append("<br>")
}
deviceStatusPumpData.extended = fromHtml(extended.toString())
}
this.deviceStatusPumpData = deviceStatusPumpData
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
class DeviceStatusOpenAPSData {
var clockSuggested = 0L
var clockEnacted = 0L
var suggested: JSONObject? = null
var enacted: JSONObject? = null
}
private fun updateOpenApsData(jsonObject: JSONObject) {
try {
val openAps = if (jsonObject.has("openaps")) jsonObject.getJSONObject("openaps") else JSONObject()
val suggested = if (openAps.has("suggested")) openAps.getJSONObject("suggested") else JSONObject()
val enacted = if (openAps.has("enacted")) openAps.getJSONObject("enacted") else JSONObject()
var clock = if (suggested.has("timestamp")) DateUtil.fromISODateString(suggested.getString("timestamp")).time else 0L
// check if this is new data
if (clock != 0L && clock > deviceStatusOpenAPSData.clockSuggested) {
deviceStatusOpenAPSData.suggested = suggested
deviceStatusOpenAPSData.clockSuggested = clock
}
clock = if (enacted.has("timestamp")) DateUtil.fromISODateString(enacted.getString("timestamp")).time else 0L
// check if this is new data
if (clock != 0L && clock > deviceStatusOpenAPSData.clockEnacted) {
deviceStatusOpenAPSData.enacted = enacted
deviceStatusOpenAPSData.clockEnacted = clock
}
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
val openApsStatus: Spanned
get() {
val string = StringBuilder()
.append("<span style=\"color:${resourceHelper.gcs(R.color.defaulttext)}\">")
.append(resourceHelper.gs(R.string.openaps_short))
.append(": </span>")
// test warning level
val level = when {
deviceStatusOpenAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_urgent_staledatavalue, 31)).msecs() < dateUtil._now() -> Levels.URGENT
deviceStatusOpenAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_staledatavalue, 16)).msecs() < dateUtil._now() -> Levels.WARN
else -> Levels.INFO
}
string.append("<span style=\"color:${level.toColor()}\">")
if (deviceStatusOpenAPSData.clockSuggested != 0L) string.append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append(" ")
string.append("</span>") // color
return fromHtml(string.toString())
}
val extendedOpenApsStatus: Spanned
get() {
val string = StringBuilder()
try {
if (deviceStatusOpenAPSData.enacted != null && deviceStatusOpenAPSData.clockEnacted != deviceStatusOpenAPSData.clockSuggested) string.append("<b>").append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockEnacted)).append("</b> ").append(deviceStatusOpenAPSData.enacted!!.getString("reason")).append("<br>")
if (deviceStatusOpenAPSData.suggested != null) string.append("<b>").append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append("</b> ").append(deviceStatusOpenAPSData.suggested!!.getString("reason")).append("<br>")
return fromHtml(string.toString())
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
return fromHtml("")
}
internal class Uploader {
var clock = 0L
var battery = 0
}
private fun updateUploaderData(jsonObject: JSONObject) {
try {
val clock =
when {
jsonObject.has("mills") -> jsonObject.getLong("mills")
jsonObject.has("created_at") -> DateUtil.fromISODateString(jsonObject.getString("created_at")).time
else -> 0L
}
val device = device
val battery: Int =
when {
jsonObject.has("uploaderBattery") -> jsonObject.getInt("uploaderBattery")
jsonObject.has("uploader") && jsonObject.getJSONObject("uploader").has("battery") -> jsonObject.getJSONObject("uploader").getInt("battery")
else -> 0
}
var uploader = uploaderMap[device]
// check if this is new data
if (clock != 0L && battery != 0 && (uploader == null || clock > uploader.clock)) {
if (uploader == null) uploader = Uploader()
uploader.battery = battery
uploader.clock = clock
uploaderMap[device] = uploader
}
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
val uploaderStatus: String
get() {
val iterator: Iterator<*> = uploaderMap.entries.iterator()
var minBattery = 100
while (iterator.hasNext()) {
val pair = iterator.next() as Map.Entry<*, *>
val uploader = pair.value as Uploader
if (minBattery > uploader.battery) minBattery = uploader.battery
}
return "$minBattery%"
}
val uploaderStatusSpanned: Spanned
get() {
val string = StringBuilder()
string.append("<span style=\"color:${resourceHelper.gcs(R.color.defaulttext)}\">")
string.append(resourceHelper.gs(R.string.uploader_short))
string.append(": </span>")
val iterator: Iterator<*> = uploaderMap.entries.iterator()
var minBattery = 100
while (iterator.hasNext()) {
val pair = iterator.next() as Map.Entry<*, *>
val uploader = pair.value as Uploader
if (minBattery > uploader.battery) minBattery = uploader.battery
}
string.append(minBattery)
string.append("%")
return fromHtml(string.toString())
}
val extendedUploaderStatus: Spanned
get() {
val string = StringBuilder()
val iterator: Iterator<*> = uploaderMap.entries.iterator()
while (iterator.hasNext()) {
val pair = iterator.next() as Map.Entry<*, *>
val uploader = pair.value as Uploader
val device = pair.key as String
string.append("<b>").append(device).append(":</b> ").append(uploader.battery).append("%<br>")
}
return fromHtml(string.toString())
}
// ********* OpenAPS data ***********
var deviceStatusOpenAPSData = DeviceStatusOpenAPSData()
val openApsTimestamp: Long
get() =
if (deviceStatusOpenAPSData.clockSuggested != 0L) {
deviceStatusOpenAPSData.clockSuggested
} else {
-1
}
// ********* Uploader data ***********
private val uploaderMap = HashMap<String, Uploader>()
fun getAPSResult(injector: HasAndroidInjector): APSResult {
val result = APSResult(injector)
result.json = deviceStatusOpenAPSData.suggested
result.date = deviceStatusOpenAPSData.clockSuggested
return result
}
}

View file

@ -3,7 +3,7 @@ package info.nightscout.androidaps.plugins.general.nsclient.data
import android.content.Context import android.content.Context
import info.nightscout.androidaps.Config import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.* import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -111,6 +111,7 @@ import javax.inject.Singleton
"activeProfile": "2016 +30%" "activeProfile": "2016 +30%"
} }
*/ */
@Suppress("SpellCheckingInspection")
@Singleton @Singleton
class NSSettingsStatus @Inject constructor( class NSSettingsStatus @Inject constructor(
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
@ -122,15 +123,30 @@ class NSSettingsStatus @Inject constructor(
private val uel: UserEntryLogger private val uel: UserEntryLogger
) { ) {
var nightscoutVersionName = ""
// ***** PUMP STATUS ****** // ***** PUMP STATUS ******
var data: JSONObject? = null private var data: JSONObject? = null
fun handleNewData(nightscoutVersionName: String, nightscoutVersionCode: Int, status: JSONObject) { /* Other received data to 2016/02/10
this.nightscoutVersionName = nightscoutVersionName {
aapsLogger.debug(LTag.NSCLIENT, "Got versions: Nightscout: $nightscoutVersionName") status: 'ok'
if (nightscoutVersionCode != 0 && nightscoutVersionCode < config.SUPPORTEDNSVERSION) { , name: env.name
, version: env.version
, versionNum: versionNum (for ver 1.2.3 contains 10203)
, serverTime: new Date().toISOString()
, apiEnabled: apiEnabled
, careportalEnabled: apiEnabled && env.settings.enable.indexOf('careportal') > -1
, boluscalcEnabled: apiEnabled && env.settings.enable.indexOf('boluscalc') > -1
, head: env.head
, settings: env.settings
, extendedSettings: ctx.plugins && ctx.plugins.extendedClientSettings ? ctx.plugins.extendedClientSettings(env.extendedSettings) : {}
, activeProfile ..... calculated from treatments or missing
}
*/
fun handleNewData(status: JSONObject) {
data = status
aapsLogger.debug(LTag.NSCLIENT, "Got versions: Nightscout: ${getVersion()}")
if (getVersionNum() < config.SUPPORTEDNSVERSION) {
val notification = Notification(Notification.OLD_NS, resourceHelper.gs(R.string.unsupportednsversion), Notification.NORMAL) val notification = Notification(Notification.OLD_NS, resourceHelper.gs(R.string.unsupportednsversion), Notification.NORMAL)
rxBus.send(EventNewNotification(notification)) rxBus.send(EventNewNotification(notification))
} else { } else {
@ -145,13 +161,10 @@ class NSSettingsStatus @Inject constructor(
if (config.NSCLIENT) copyStatusLightsNsSettings(null) if (config.NSCLIENT) copyStatusLightsNsSettings(null)
} }
fun getName(): String? = fun getVersion(): String =
JsonHelper.safeGetStringAllowNull(data, "name", null) JsonHelper.safeGetStringAllowNull(data, "version", null) ?: "UNKNOWN"
fun getVersion(): String? = private fun getVersionNum(): Int =
JsonHelper.safeGetStringAllowNull(data, "version", null)
fun getVersionNum(): Int =
JsonHelper.safeGetInt(data, "versionNum") JsonHelper.safeGetInt(data, "versionNum")
private fun getSettings() = private fun getSettings() =
@ -162,13 +175,13 @@ class NSSettingsStatus @Inject constructor(
// valid property is "warn" or "urgent" // valid property is "warn" or "urgent"
// plugings "iage" "sage" "cage" "pbage" // plugings "iage" "sage" "cage" "pbage"
fun getExtendedWarnValue(plugin: String, property: String): Double? { private fun getExtendedWarnValue(plugin: String, property: String): Double? {
val extendedSettings = getExtendedSettings() ?: return null val extendedSettings = getExtendedSettings() ?: return null
val pluginJson = extendedSettings.optJSONObject(plugin) ?: return null val pluginJson = extendedSettings.optJSONObject(plugin) ?: return null
try { return try {
return pluginJson.getDouble(property) pluginJson.getDouble(property)
} catch (e: Exception) { } catch (e: Exception) {
return null null
} }
} }
@ -176,7 +189,7 @@ class NSSettingsStatus @Inject constructor(
// "bgTargetTop": 180, // "bgTargetTop": 180,
// "bgTargetBottom": 72, // "bgTargetBottom": 72,
// "bgLow": 71 // "bgLow": 71
fun getSettingsThreshold(what: String): Double? { private fun getSettingsThreshold(what: String): Double? {
val threshold = JsonHelper.safeGetJSONObject(getSettings(), "thresholds", null) val threshold = JsonHelper.safeGetJSONObject(getSettings(), "thresholds", null)
return JsonHelper.safeGetDoubleAllowNull(threshold, what) return JsonHelper.safeGetDoubleAllowNull(threshold, what)
} }
@ -215,9 +228,6 @@ class NSSettingsStatus @Inject constructor(
private fun extendedPumpSettings(): JSONObject? = private fun extendedPumpSettings(): JSONObject? =
JsonHelper.safeGetJSONObject(getExtendedSettings(), "pump", null) JsonHelper.safeGetJSONObject(getExtendedSettings(), "pump", null)
fun pumpExtendedSettingsEnabledAlerts(): Boolean =
JsonHelper.safeGetBoolean(extendedPumpSettings(), "enableAlerts")
fun pumpExtendedSettingsFields(): String = fun pumpExtendedSettingsFields(): String =
JsonHelper.safeGetString(extendedPumpSettings(), "fields", "") JsonHelper.safeGetString(extendedPumpSettings(), "fields", "")

View file

@ -0,0 +1,32 @@
package info.nightscout.androidaps.plugins.general.nsclient.data
import info.nightscout.androidaps.utils.JsonHelper
import org.json.JSONObject
/**
*
* {"mgdl":105,"mills":1455136282375,"device":"xDrip-BluetoothWixel","direction":"Flat","filtered":98272,"unfiltered":98272,"noise":1,"rssi":100}
*/
@Suppress("SpellCheckingInspection")
class NSSgv(val data: JSONObject) {
val mgdl: Int?
get() = JsonHelper.safeGetIntAllowNull(data, "mgdl")
val filtered: Int?
get() = JsonHelper.safeGetIntAllowNull(data, "filtered")
val unfiltered: Int?
get() = JsonHelper.safeGetIntAllowNull(data, "unfiltered")
val noise: Int?
get() = JsonHelper.safeGetIntAllowNull(data, "noise")
val rssi: Int?
get() = JsonHelper.safeGetIntAllowNull(data, "rssi")
val mills: Long?
get() = JsonHelper.safeGetLongAllowNull(data, "mills")
val device: String?
get() = JsonHelper.safeGetStringAllowNull(data, "device", null)
val direction: String?
get() = JsonHelper.safeGetStringAllowNull(data, "direction", null)
val id: String?
get() = JsonHelper.safeGetStringAllowNull(data, "_id", null)
}

View file

@ -1,127 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient.data;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import java.util.Date;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
public class NSTreatment {
private static final Logger log = StacktraceLoggerWrapper.getLogger(LTag.NSCLIENT);
private final JSONObject data;
private String action = null; // "update", "remove" or null (add)
public NSTreatment(JSONObject obj) {
this.data = obj;
this.action = getStringOrNull("action");
this.data.remove("action");
}
private String getStringOrNull(String key) {
String ret = null;
if (data.has(key)) {
try {
ret = data.getString(key);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
private Double getDoubleOrNull(String key) {
Double ret = null;
if (data.has(key)) {
try {
ret = data.getDouble(key);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
private Integer getIntegerOrNull(String key) {
Integer ret = null;
if (data.has(key)) {
try {
ret = data.getInt(key);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
private Long getLongOrNull(String key) {
Long ret = null;
if (data.has(key)) {
try {
ret = data.getLong(key);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
private Date getDateOrNull(String key) {
Date ret = null;
if (data.has(key)) {
try {
ret = new Date(data.getString(key));
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
public String getAction() {
return action;
}
public JSONObject getData() {
return data;
}
public String get_id() {
return getStringOrNull("_id");
}
public String getEnteredBy() {
return getStringOrNull("enteredBy");
}
public String getEventType() {
return getStringOrNull("eventType");
}
public Integer getHapp_id() {
return getIntegerOrNull("happ_id");
}
public Integer getDuration() {
return getIntegerOrNull("duration");
}
public Integer getMgdl() {
return getIntegerOrNull("mgdl");
}
public Double getAbsolute() {
return getDoubleOrNull("absolute");
}
public Long getMills() {
return getLongOrNull("mills");
}
public Date getCreated_at() {
return getDateOrNull("created_at");
}
}

View file

@ -11,6 +11,7 @@ import android.os.PowerManager;
import android.os.SystemClock; import android.os.SystemClock;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.work.OneTimeWorkRequest;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
@ -34,16 +35,18 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.DbRequest; import info.nightscout.androidaps.db.DbRequest;
import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventConfigBuilderChange; import info.nightscout.androidaps.events.EventConfigBuilderChange;
import info.nightscout.androidaps.events.EventNsFood;
import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileStore;
import info.nightscout.androidaps.interfaces.UploadQueueInterface; import info.nightscout.androidaps.interfaces.UploadQueueInterface;
import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.food.FoodPlugin;
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.NSClientPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker;
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck; 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.NSAuthAck;
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck; import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck;
@ -51,8 +54,6 @@ 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.NSAlarm;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; 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.data.NSSettingsStatus;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSTreatment;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog; 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.EventNSClientRestart;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus;
@ -61,6 +62,9 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNo
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; 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.Notification;
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction; 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.services.Intents;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.FabricPrivacy;
@ -91,14 +95,13 @@ public class NSClientService extends DaggerService {
@Inject Config config; @Inject Config config;
@Inject DateUtil dateUtil; @Inject DateUtil dateUtil;
@Inject UploadQueueInterface uploadQueue; @Inject UploadQueueInterface uploadQueue;
@Inject DataWorker dataWorker;
private final CompositeDisposable disposable = new CompositeDisposable(); private final CompositeDisposable disposable = new CompositeDisposable();
static public PowerManager.WakeLock mWakeLock; static public PowerManager.WakeLock mWakeLock;
private final IBinder mBinder = new NSClientService.LocalBinder(); private final IBinder mBinder = new NSClientService.LocalBinder();
static ProfileStore profileStore;
static public Handler handler; static public Handler handler;
public static Socket mSocket; public static Socket mSocket;
@ -108,9 +111,6 @@ public class NSClientService extends DaggerService {
private static Integer connectCounter = 0; private static Integer connectCounter = 0;
public static String nightscoutVersionName = "";
public static Integer nightscoutVersionCode = 0;
private boolean nsEnabled = false; private boolean nsEnabled = false;
static public String nsURL = ""; static public String nsURL = "";
private String nsAPISecret = ""; private String nsAPISecret = "";
@ -535,185 +535,142 @@ public class NSClientService extends DaggerService {
boolean isFull = !isDelta; boolean isFull = !isDelta;
rxBus.send(new EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + (isDelta ? " delta" : " full"))); rxBus.send(new EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + (isDelta ? " delta" : " full")));
if (data.has("profiles")) {
JSONArray profiles = data.getJSONArray("profiles");
if (profiles.length() > 0) {
JSONObject profile = (JSONObject) profiles.get(profiles.length() - 1);
profileStore = new ProfileStore(injector, profile);
broadcastProfile = true;
rxBus.send(new EventNSClientNewLog("PROFILE", "profile received"));
}
}
if (data.has("status")) { if (data.has("status")) {
JSONObject status = data.getJSONObject("status"); JSONObject status = data.getJSONObject("status");
nsSettingsStatus.setData(status); nsSettingsStatus.handleNewData(status);
if (!status.has("versionNum")) {
if (status.getInt("versionNum") < config.getSUPPORTEDNSVERSION()) {
rxBus.send(new EventNSClientNewLog("ERROR", "Unsupported Nightscout version !!!!"));
}
} else {
nightscoutVersionName = nsSettingsStatus.getVersion();
nightscoutVersionCode = nsSettingsStatus.getVersionNum();
}
nsSettingsStatus.handleNewData(nightscoutVersionName, nightscoutVersionCode, status);
/* Other received data to 2016/02/10
{
status: 'ok'
, name: env.name
, version: env.version
, versionNum: versionNum (for ver 1.2.3 contains 10203)
, serverTime: new Date().toISOString()
, apiEnabled: apiEnabled
, careportalEnabled: apiEnabled && env.settings.enable.indexOf('careportal') > -1
, boluscalcEnabled: apiEnabled && env.settings.enable.indexOf('boluscalc') > -1
, head: env.head
, settings: env.settings
, extendedSettings: ctx.plugins && ctx.plugins.extendedClientSettings ? ctx.plugins.extendedClientSettings(env.extendedSettings) : {}
, activeProfile ..... calculated from treatments or missing
}
*/
} else if (!isDelta) { } else if (!isDelta) {
rxBus.send(new EventNSClientNewLog("ERROR", "Unsupported Nightscout version !!!!")); rxBus.send(new EventNSClientNewLog("ERROR", "Unsupported Nightscout version !!!!"));
} }
// If new profile received or change detected broadcast it if (data.has("profiles")) {
if (broadcastProfile && profileStore != null) { JSONArray profiles = data.getJSONArray("profiles");
handleNewProfile(profileStore, isDelta); if (profiles.length() > 0) {
rxBus.send(new EventNSClientNewLog("PROFILE", "broadcasting")); // 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")) { if (data.has("treatments")) {
JSONArray treatments = data.getJSONArray("treatments"); JSONArray treatments = data.getJSONArray("treatments");
JSONArray removedTreatments = new JSONArray(); JSONArray removedTreatments = new JSONArray();
JSONArray updatedTreatments = new JSONArray(); JSONArray addedOrUpdatedTreatments = new JSONArray();
JSONArray addedTreatments = new JSONArray();
if (treatments.length() > 0) if (treatments.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments")); rxBus.send(new EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments"));
for (Integer index = 0; index < treatments.length(); index++) { for (Integer index = 0; index < treatments.length(); index++) {
JSONObject jsonTreatment = treatments.getJSONObject(index); JSONObject jsonTreatment = treatments.getJSONObject(index);
NSTreatment treatment = new NSTreatment(jsonTreatment); String action = JsonHelper.safeGetStringAllowNull(jsonTreatment, "action", null);
long mills = JsonHelper.safeGetLong(jsonTreatment, "mills");
// remove from upload queue if Ack is failing if (action == null) addedOrUpdatedTreatments.put(jsonTreatment);
uploadQueue.removeByNsClientIdIfExists(jsonTreatment); else if (action.equals("update"))
//Find latest date in treatment addedOrUpdatedTreatments.put(jsonTreatment);
if (treatment.getMills() != null && treatment.getMills() < System.currentTimeMillis()) else if (action.equals("remove") && mills > dateUtil._now() - T.days(1).msecs()) // handle 1 day old deletions only
if (treatment.getMills() > latestDateInReceivedData) removedTreatments.put(jsonTreatment);
latestDateInReceivedData = treatment.getMills();
if (treatment.getAction() == null) {
addedTreatments.put(jsonTreatment);
} else if (treatment.getAction().equals("update")) {
updatedTreatments.put(jsonTreatment);
} else if (treatment.getAction().equals("remove")) {
if (treatment.getMills() != null && treatment.getMills() > System.currentTimeMillis() - 24 * 60 * 60 * 1000L) // handle 1 day old deletions only
removedTreatments.put(jsonTreatment);
}
} }
if (removedTreatments.length() > 0) { if (removedTreatments.length() > 0) {
handleRemovedTreatment(removedTreatments, isDelta); 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 (updatedTreatments.length() > 0) { if (addedOrUpdatedTreatments.length() > 0) {
handleChangedTreatment(updatedTreatments, isDelta); dataWorker.enqueue(
} new OneTimeWorkRequest.Builder(NSClientAddUpdateWorker.class)
if (addedTreatments.length() > 0) { .setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null))
handleNewTreatment(addedTreatments, isDelta); .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")) { if (data.has("devicestatus")) {
JSONArray devicestatuses = data.getJSONArray("devicestatus"); JSONArray devicestatuses = data.getJSONArray("devicestatus");
if (devicestatuses.length() > 0) { if (devicestatuses.length() > 0) {
rxBus.send(new EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " devicestatuses")); rxBus.send(new EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses"));
for (Integer index = 0; index < devicestatuses.length(); index++) {
JSONObject jsonStatus = devicestatuses.getJSONObject(index);
// remove from upload queue if Ack is failing
uploadQueue.removeByNsClientIdIfExists(jsonStatus);
}
nsDeviceStatus.handleNewData(devicestatuses); nsDeviceStatus.handleNewData(devicestatuses);
} }
} }
if (data.has("food")) { if (data.has("food")) {
JSONArray foods = data.getJSONArray("food"); JSONArray foods = data.getJSONArray("food");
JSONArray removedFoods = new JSONArray();
JSONArray updatedFoods = new JSONArray();
JSONArray addedFoods = new JSONArray();
if (foods.length() > 0) if (foods.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods")); rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods"));
for (Integer index = 0; index < foods.length(); index++) { dataWorker.enqueue(
JSONObject jsonFood = foods.getJSONObject(index); new OneTimeWorkRequest.Builder(FoodPlugin.FoodWorker.class)
.setInputData(dataWorker.storeInputData(foods, null))
// remove from upload queue if Ack is failing .build());
uploadQueue.removeByNsClientIdIfExists(jsonFood);
String action = JsonHelper.safeGetString(jsonFood, "action");
if (action == null) {
addedFoods.put(jsonFood);
} else if (action.equals("update")) {
updatedFoods.put(jsonFood);
} else if (action.equals("remove")) {
removedFoods.put(jsonFood);
}
}
if (removedFoods.length() > 0) {
EventNsFood evt = new EventNsFood(EventNsFood.Companion.getREMOVE(), removedFoods);
rxBus.send(evt);
}
if (updatedFoods.length() > 0) {
EventNsFood evt = new EventNsFood(EventNsFood.Companion.getUPDATE(), updatedFoods);
rxBus.send(evt);
}
if (addedFoods.length() > 0) {
EventNsFood evt = new EventNsFood(EventNsFood.Companion.getADD(), addedFoods);
rxBus.send(evt);
}
} }
//noinspection SpellCheckingInspection
if (data.has("mbgs")) { if (data.has("mbgs")) {
JSONArray mbgs = data.getJSONArray("mbgs"); JSONArray mbgArray = data.getJSONArray("mbgs");
if (mbgs.length() > 0) if (mbgArray.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + mbgs.length() + " mbgs")); rxBus.send(new EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs"));
for (Integer index = 0; index < mbgs.length(); index++) { dataWorker.enqueue(
JSONObject jsonMbg = mbgs.getJSONObject(index); new OneTimeWorkRequest.Builder(NSClientMbgWorker.class)
// remove from upload queue if Ack is failing .setInputData(dataWorker.storeInputData(mbgArray, null))
uploadQueue.removeByNsClientIdIfExists(jsonMbg); .build());
}
handleNewMbg(mbgs, isDelta);
} }
if (data.has("cals")) { if (data.has("cals")) {
JSONArray cals = data.getJSONArray("cals"); JSONArray cals = data.getJSONArray("cals");
if (cals.length() > 0) if (cals.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + cals.length() + " cals")); rxBus.send(new EventNSClientNewLog("DATA", "received " + cals.length() + " cals"));
// Retreive actual calibration // Calibrations ignored
for (Integer index = 0; index < cals.length(); index++) {
// remove from upload queue if Ack is failing
uploadQueue.removeByNsClientIdIfExists(cals.optJSONObject(index));
}
handleNewCal(cals, isDelta);
} }
if (data.has("sgvs")) { if (data.has("sgvs")) {
JSONArray sgvs = data.getJSONArray("sgvs"); JSONArray sgvs = data.getJSONArray("sgvs");
if (sgvs.length() > 0) if (sgvs.length() > 0)
rxBus.send(new EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs")); rxBus.send(new EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs"));
for (int index = 0; index < sgvs.length(); index++) {
JSONObject jsonSgv = sgvs.getJSONObject(index); dataWorker.enqueue(new OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker.class)
// rxBus.send(new EventNSClientNewLog("DATA", "svg " + sgvs.getJSONObject(index).toString()); .setInputData(dataWorker.storeInputData(sgvs, null))
NSSgv sgv = new NSSgv(jsonSgv); .build());
// Handle new sgv here
// remove from upload queue if Ack is failing List<JSONArray> splitted = splitArray(sgvs);
uploadQueue.removeByNsClientIdIfExists(jsonSgv); if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
//Find latest date in sgv for (JSONArray part : splitted) {
if (sgv.getMills() != null && sgv.getMills() < System.currentTimeMillis()) Bundle bundle = new Bundle();
if (sgv.getMills() > latestDateInReceivedData) bundle.putString("sgvs", part.toString());
latestDateInReceivedData = sgv.getMills(); bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_NEW_SGV);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
}
} }
// Was that sgv more less 5 mins ago ?
if ((System.currentTimeMillis() - latestDateInReceivedData) / (60 * 1000L) < 5L) {
rxBus.send(new EventDismissNotification(Notification.NS_ALARM));
rxBus.send(new EventDismissNotification(Notification.NS_URGENT_ALARM));
}
handleNewSgv(sgvs, isDelta);
} }
rxBus.send(new EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData))); rxBus.send(new EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)));
} catch (JSONException e) { } catch (JSONException e) {
@ -881,71 +838,6 @@ public class NSClientService extends DaggerService {
} }
} }
public void handleNewCal(JSONArray cals, boolean isDelta) {
Bundle bundle = new Bundle();
bundle.putString("cals", cals.toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_NEW_CAL);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
public void handleNewMbg(JSONArray mbgs, boolean isDelta) {
Bundle bundle = new Bundle();
bundle.putString("mbgs", mbgs.toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_NEW_MBG);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
public void handleNewProfile(ProfileStore profile, boolean isDelta) {
Bundle bundle = new Bundle();
bundle.putString("profile", profile.getData().toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_NEW_PROFILE);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
bundle = new Bundle();
bundle.putString("profile", profile.getData().toString());
bundle.putBoolean("delta", isDelta);
intent = new Intent(Intents.ACTION_NEW_PROFILE);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
this.sendBroadcast(intent);
}
}
public void handleNewSgv(JSONArray sgvs, boolean isDelta) {
List<JSONArray> splitted = splitArray(sgvs);
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);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
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);
this.sendBroadcast(intent);
}
}
}
public void handleNewTreatment(JSONArray treatments, boolean isDelta) { public void handleNewTreatment(JSONArray treatments, boolean isDelta) {
List<JSONArray> splitted = splitArray(treatments); List<JSONArray> splitted = splitArray(treatments);
for (JSONArray part : splitted) { for (JSONArray part : splitted) {
@ -972,54 +864,6 @@ public class NSClientService extends DaggerService {
} }
} }
public void handleChangedTreatment(JSONArray treatments, boolean isDelta) {
List<JSONArray> splitted = splitArray(treatments);
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);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
splitted = splitArray(treatments);
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);
this.getApplicationContext().sendBroadcast(intent);
}
}
}
public void handleRemovedTreatment(JSONArray treatments, boolean isDelta) {
Bundle bundle = new Bundle();
bundle.putString("treatments", treatments.toString());
bundle.putBoolean("delta", isDelta);
Intent intent = new Intent(Intents.ACTION_REMOVED_TREATMENT);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
bundle = new Bundle();
bundle.putString("treatments", treatments.toString());
bundle.putBoolean("delta", isDelta);
intent = new Intent(Intents.ACTION_REMOVED_TREATMENT);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
this.getApplicationContext().sendBroadcast(intent);
}
}
public List<JSONArray> splitArray(JSONArray array) { public List<JSONArray> splitArray(JSONArray array) {
List<JSONArray> ret = new ArrayList<>(); List<JSONArray> ret = new ArrayList<>();
try { try {

View file

@ -839,7 +839,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
val toTime: Long val toTime: Long
val fromTime: Long val fromTime: Long
val endTime: Long val endTime: Long
val apsResult = if (config.APS) lastRun?.constraintsProcessed else NSDeviceStatus.getAPSResult(injector) val apsResult = if (config.APS) lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) { if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt() var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
predictionHours = min(2, predictionHours) predictionHours = min(2, predictionHours)

View file

@ -36,11 +36,10 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.receivers.BundleStore import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.receivers.DataReceiver import info.nightscout.androidaps.receivers.DataReceiver
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.extensions.valueToUnitsString import info.nightscout.androidaps.utils.extensions.valueToUnitsString
@ -176,7 +175,7 @@ class SmsCommunicatorPlugin @Inject constructor(
) : Worker(context, params) { ) : Worker(context, params) {
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
init { init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -184,7 +183,7 @@ class SmsCommunicatorPlugin @Inject constructor(
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
override fun doWork(): Result { override fun doWork(): Result {
val bundle = bundleStore.pickup(inputData.getLong(DataReceiver.STORE_KEY, -1)) val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure() ?: return Result.failure()
val format = bundle.getString("format") ?: return Result.failure() val format = bundle.getString("format") ?: return Result.failure()
val pdus = bundle["pdus"] as Array<*> val pdus = bundle["pdus"] as Array<*>
@ -731,7 +730,7 @@ class SmsCommunicatorPlugin @Inject constructor(
var replyText = resourceHelper.gs(R.string.smscommunicator_extendedcancelfailed) var replyText = resourceHelper.gs(R.string.smscommunicator_extendedcancelfailed)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText)) sendSMS(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true),ValueWithUnit(R.string.smscommunicator_extendedcanceled, Units.R_String)) uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedcanceled, Units.R_String))
} }
} }
}) })
@ -758,14 +757,16 @@ class SmsCommunicatorPlugin @Inject constructor(
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
if (config.APS) if (config.APS)
uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedset, 2), ValueWithUnit(aDouble ?:0.0, Units.U), ValueWithUnit(duration, Units.M), ValueWithUnit(R.string.loopsuspended, Units.R_String)) uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedset, 2), ValueWithUnit(aDouble
else ?: 0.0, Units.U), ValueWithUnit(duration, Units.M), ValueWithUnit(R.string.loopsuspended, Units.R_String))
uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedset, 2), ValueWithUnit(aDouble ?:0.0, Units.U), ValueWithUnit(duration, Units.M)) else
uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedset, 2), ValueWithUnit(aDouble
?: 0.0, Units.U), ValueWithUnit(duration, Units.M))
} else { } else {
var replyText = resourceHelper.gs(R.string.smscommunicator_extendedfailed) var replyText = resourceHelper.gs(R.string.smscommunicator_extendedfailed)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText)) sendSMS(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true),ValueWithUnit(R.string.smscommunicator_extendedfailed, Units.R_String)) uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedfailed, Units.R_String))
} }
} }
}) })
@ -883,12 +884,14 @@ class SmsCommunicatorPlugin @Inject constructor(
var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsset, 1), ValueWithUnit(anInteger ?:0,Units.G)) uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsset, 1), ValueWithUnit(anInteger
?: 0, Units.G))
} else { } else {
var replyText = resourceHelper.gs(R.string.smscommunicator_carbsfailed, anInteger) var replyText = resourceHelper.gs(R.string.smscommunicator_carbsfailed, anInteger)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText)) sendSMS(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsfailed, 1), ValueWithUnit(anInteger ?:0,Units.G)) uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsfailed, 1), ValueWithUnit(anInteger
?: 0, Units.G))
} }
} }
}) })
@ -897,7 +900,8 @@ class SmsCommunicatorPlugin @Inject constructor(
var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsset, 1), ValueWithUnit(anInteger ?:0,Units.G)) uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsset, 1), ValueWithUnit(anInteger
?: 0, Units.G))
} }
} }
}) })

View file

@ -715,7 +715,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
openApsStatus = loopPlugin.getLastRun() != null && loopPlugin.getLastRun().getLastTBREnact() != 0 ? loopPlugin.getLastRun().getLastTBREnact() : -1; openApsStatus = loopPlugin.getLastRun() != null && loopPlugin.getLastRun().getLastTBREnact() != 0 ? loopPlugin.getLastRun().getLastTBREnact() : -1;
} else { } else {
//NSClient or remote //NSClient or remote
openApsStatus = NSDeviceStatus.getOpenApsTimestamp(); openApsStatus = nsDeviceStatus.getOpenApsTimestamp();
} }
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_STATUS_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_STATUS_PATH);

View file

@ -17,8 +17,7 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.profile.ns.events.EventNSProfileUpdateGUI import info.nightscout.androidaps.plugins.profile.ns.events.EventNSProfileUpdateGUI
import info.nightscout.androidaps.receivers.BundleStore import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.receivers.DataReceiver
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONObject import org.json.JSONObject
@ -89,26 +88,23 @@ class NSProfilePlugin @Inject constructor(
@Inject lateinit var nsProfilePlugin: NSProfilePlugin @Inject lateinit var nsProfilePlugin: NSProfilePlugin
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
init { init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
} }
override fun doWork(): Result { override fun doWork(): Result {
val bundle = bundleStore.pickup(inputData.getLong(DataReceiver.STORE_KEY, -1)) val profileString = dataWorker.pickupJSONObject(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure() ?: return Result.failure()
bundle.getString("profile")?.let { profileString -> nsProfilePlugin.profile = ProfileStore(injector, profileString)
nsProfilePlugin.profile = ProfileStore(injector, JSONObject(profileString)) nsProfilePlugin.storeNSProfile()
nsProfilePlugin.storeNSProfile() if (nsProfilePlugin.isEnabled()) {
if (nsProfilePlugin.isEnabled()) { rxBus.send(EventProfileStoreChanged())
rxBus.send(EventProfileStoreChanged()) rxBus.send(EventNSProfileUpdateGUI())
rxBus.send(EventNSProfileUpdateGUI())
}
aapsLogger.debug(LTag.PROFILE, "Received profileStore: ${nsProfilePlugin.profile}")
return Result.success()
} }
return Result.failure() aapsLogger.debug(LTag.PROFILE, "Received profileStore: ${nsProfilePlugin.profile}")
return Result.success()
} }
} }
} }

View file

@ -40,7 +40,7 @@ import javax.inject.Singleton
import kotlin.math.min import kotlin.math.min
@Singleton @Singleton
class VirtualPumpPlugin @Inject constructor( open class VirtualPumpPlugin @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper, private val rxBus: RxBusWrapper,

View file

@ -21,8 +21,7 @@ import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.receivers.BundleStore import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.receivers.DataReceiver
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.XDripBroadcast
@ -84,7 +83,7 @@ class DexcomPlugin @Inject constructor(
@Inject lateinit var dexcomPlugin: DexcomPlugin @Inject lateinit var dexcomPlugin: DexcomPlugin
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
@Inject lateinit var broadcastToXDrip: XDripBroadcast @Inject lateinit var broadcastToXDrip: XDripBroadcast
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@ -93,8 +92,10 @@ class DexcomPlugin @Inject constructor(
} }
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!dexcomPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() if (!dexcomPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
val bundle = bundleStore.pickup(inputData.getLong(DataReceiver.STORE_KEY, -1)) val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure() ?: return Result.failure()
try { try {
val sourceSensor = when (bundle.getString("sensorType") ?: "") { val sourceSensor = when (bundle.getString("sensorType") ?: "") {
@ -142,23 +143,37 @@ class DexcomPlugin @Inject constructor(
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) { if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.uploadBg(it, sourceSensor.text) nsUpload.uploadBg(it, sourceSensor.text)
//aapsLogger.debug("XXXXX: dbAdd $it")
} }
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
} }
result.updated.forEach { result.updated.forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) { if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.updateBg(it, sourceSensor.text) nsUpload.updateBg(it, sourceSensor.text)
//aapsLogger.debug("XXXXX: dpUpdate $it")
} }
aapsLogger.debug(LTag.BGSOURCE, "Updated bg $it")
}
result.sensorInsertionsInserted.forEach {
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.uploadEvent(it)
}
aapsLogger.debug(LTag.BGSOURCE, "Inserted sensor insertion $it")
}
result.calibrationsInserted.forEach {
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.uploadEvent(it)
}
aapsLogger.debug(LTag.BGSOURCE, "Inserted calibration $it")
} }
}, { }, {
aapsLogger.error("Error while saving values from Dexcom App", it) aapsLogger.error("Error while saving values from Dexcom App", it)
ret = Result.failure()
}) })
} catch (e: Exception) { } catch (e: Exception) {
aapsLogger.error("Error while processing intent from Dexcom App", e) aapsLogger.error("Error while processing intent from Dexcom App", e)
ret = Result.failure()
} }
return Result.success() return ret
} }
} }

View file

@ -4,7 +4,6 @@ import android.content.Context
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
@ -18,14 +17,11 @@ import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.receivers.BundleStore import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.receivers.DataReceiver
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -48,13 +44,6 @@ class EversensePlugin @Inject constructor(
override var sensorBatteryLevel = -1 override var sensorBatteryLevel = -1
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection // cannot be inner class because of needed injection
class EversenseWorker( class EversenseWorker(
context: Context, context: Context,
@ -67,7 +56,7 @@ class EversensePlugin @Inject constructor(
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast @Inject lateinit var broadcastToXDrip: XDripBroadcast
@ -76,8 +65,10 @@ class EversensePlugin @Inject constructor(
} }
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!eversensePlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() if (!eversensePlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
val bundle = bundleStore.pickup(inputData.getLong(DataReceiver.STORE_KEY, -1)) val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure() ?: return Result.failure()
if (bundle.containsKey("currentCalibrationPhase")) aapsLogger.debug(LTag.BGSOURCE, "currentCalibrationPhase: " + bundle.getString("currentCalibrationPhase")) if (bundle.containsKey("currentCalibrationPhase")) aapsLogger.debug(LTag.BGSOURCE, "currentCalibrationPhase: " + bundle.getString("currentCalibrationPhase"))
if (bundle.containsKey("placementModeInProgress")) aapsLogger.debug(LTag.BGSOURCE, "placementModeInProgress: " + bundle.getBoolean("placementModeInProgress")) if (bundle.containsKey("placementModeInProgress")) aapsLogger.debug(LTag.BGSOURCE, "placementModeInProgress: " + bundle.getBoolean("placementModeInProgress"))
@ -116,15 +107,20 @@ class EversensePlugin @Inject constructor(
trendArrow = GlucoseValue.TrendArrow.NONE, trendArrow = GlucoseValue.TrendArrow.NONE,
sourceSensor = GlucoseValue.SourceSensor.EVERSENSE sourceSensor = GlucoseValue.SourceSensor.EVERSENSE
) )
eversensePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
savedValues.inserted.forEach { .doOnError {
broadcastToXDrip(it) aapsLogger.error("Error while saving values from Eversense App", it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) ret = Result.failure()
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.EVERSENSE.text) }
.blockingGet()
.also { savedValues ->
savedValues.inserted.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.EVERSENSE.text)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
}
} }
}, {
aapsLogger.error("Error while saving values from Eversense App", it)
})
} }
} }
if (bundle.containsKey("calibrationGlucoseLevels")) { if (bundle.containsKey("calibrationGlucoseLevels")) {
@ -136,22 +132,29 @@ class EversensePlugin @Inject constructor(
aapsLogger.debug(LTag.BGSOURCE, "calibrationTimestamps" + Arrays.toString(calibrationTimestamps)) aapsLogger.debug(LTag.BGSOURCE, "calibrationTimestamps" + Arrays.toString(calibrationTimestamps))
aapsLogger.debug(LTag.BGSOURCE, "calibrationRecordNumbers" + Arrays.toString(calibrationRecordNumbers)) aapsLogger.debug(LTag.BGSOURCE, "calibrationRecordNumbers" + Arrays.toString(calibrationRecordNumbers))
for (i in calibrationGlucoseLevels.indices) { for (i in calibrationGlucoseLevels.indices) {
eversensePlugin.disposable += repository.runTransactionForResult(InsertTherapyEventIfNewTransaction( repository.runTransactionForResult(InsertTherapyEventIfNewTransaction(
timestamp = calibrationTimestamps[i], timestamp = calibrationTimestamps[i],
type = TherapyEvent.Type.FINGER_STICK_BG_VALUE, type = TherapyEvent.Type.FINGER_STICK_BG_VALUE,
glucose = calibrationGlucoseLevels[i].toDouble(), glucose = calibrationGlucoseLevels[i].toDouble(),
glucoseType = TherapyEvent.MeterType.FINGER, glucoseType = TherapyEvent.MeterType.FINGER,
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL, glucoseUnit = TherapyEvent.GlucoseUnit.MGDL,
enteredBy = "AndroidAPS-Eversense" enteredBy = "AndroidAPS-Eversense"
)).subscribe({ result -> ))
result.inserted.forEach { nsUpload.uploadEvent(it) } .doOnError {
}, { aapsLogger.error(LTag.BGSOURCE, "Error while saving therapy event", it)
aapsLogger.error(LTag.BGSOURCE, "Error while saving therapy event", it) ret = Result.failure()
}) }
.blockingGet()
.also { result ->
result.inserted.forEach {
nsUpload.uploadEvent(it)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
}
}
} }
} }
} }
return Result.success() return ret
} }
} }
} }

View file

@ -18,8 +18,6 @@ import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -38,13 +36,6 @@ class GlimpPlugin @Inject constructor(
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
), BgSourceInterface { ), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection // cannot be inner class because of needed injection
class GlimpWorker( class GlimpWorker(
context: Context, context: Context,
@ -64,6 +55,8 @@ class GlimpPlugin @Inject constructor(
} }
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!glimpPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() if (!glimpPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
aapsLogger.debug(LTag.BGSOURCE, "Received Glimp Data: $inputData}") aapsLogger.debug(LTag.BGSOURCE, "Received Glimp Data: $inputData}")
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>() val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
@ -75,16 +68,21 @@ class GlimpPlugin @Inject constructor(
trendArrow = GlucoseValue.TrendArrow.fromString(inputData.getString("myTrend")), trendArrow = GlucoseValue.TrendArrow.fromString(inputData.getString("myTrend")),
sourceSensor = GlucoseValue.SourceSensor.GLIMP sourceSensor = GlucoseValue.SourceSensor.GLIMP
) )
glimpPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
savedValues.inserted.forEach { .doOnError {
broadcastToXDrip(it) aapsLogger.error("Error while saving values from Glimp App", it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) ret = Result.failure()
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.GLIMP.text)
} }
}, { .blockingGet()
aapsLogger.error("Error while saving values from Glimp App", it) .also { savedValues ->
}) savedValues.inserted.forEach {
return Result.success() broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.GLIMP.text)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
}
}
return ret
} }
} }
} }

View file

@ -15,13 +15,11 @@ import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.receivers.BundleStore import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
import javax.inject.Inject import javax.inject.Inject
@ -41,13 +39,6 @@ class MM640gPlugin @Inject constructor(
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
), BgSourceInterface { ), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection // cannot be inner class because of needed injection
class MM640gWorker( class MM640gWorker(
context: Context, context: Context,
@ -60,7 +51,7 @@ class MM640gPlugin @Inject constructor(
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast @Inject lateinit var broadcastToXDrip: XDripBroadcast
@ -69,6 +60,8 @@ class MM640gPlugin @Inject constructor(
} }
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!mM640gPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() if (!mM640gPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
val collection = inputData.getString("collection") ?: return Result.failure() val collection = inputData.getString("collection") ?: return Result.failure()
if (collection == "entries") { if (collection == "entries") {
@ -93,21 +86,27 @@ class MM640gPlugin @Inject constructor(
else -> aapsLogger.debug(LTag.BGSOURCE, "Unknown entries type: $type") else -> aapsLogger.debug(LTag.BGSOURCE, "Unknown entries type: $type")
} }
} }
mM640gPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
.doOnError {
aapsLogger.error("Error while saving values from Eversense App", it)
ret = Result.failure()
}
.blockingGet()
.also { savedValues ->
savedValues.all().forEach { savedValues.all().forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.MM_600_SERIES.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.MM_600_SERIES.text)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
} }
}, { }
aapsLogger.error("Error while saving values from Eversense App", it)
})
} catch (e: JSONException) { } catch (e: JSONException) {
aapsLogger.error("Exception: ", e) aapsLogger.error("Exception: ", e)
ret = Result.failure()
} }
} }
} }
return Result.success() return ret
} }
} }
} }

View file

@ -15,16 +15,18 @@ import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv
import info.nightscout.androidaps.receivers.BundleStore import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -55,13 +57,6 @@ class NSClientSourcePlugin @Inject constructor(
} }
} }
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
override fun advancedFilteringSupported(): Boolean { override fun advancedFilteringSupported(): Boolean {
return isAdvancedFilteringEnabled return isAdvancedFilteringEnabled
} }
@ -89,65 +84,85 @@ class NSClientSourcePlugin @Inject constructor(
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast @Inject lateinit var broadcastToXDrip: XDripBroadcast
@Inject lateinit var dexcomPlugin: DexcomPlugin @Inject lateinit var dexcomPlugin: DexcomPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
init { init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
} }
private fun toGv(jsonObject: JSONObject): CgmSourceTransaction.TransactionGlucoseValue { private fun toGv(jsonObject: JSONObject): CgmSourceTransaction.TransactionGlucoseValue? {
val sgv = NSSgv(jsonObject) val sgv = NSSgv(jsonObject)
return CgmSourceTransaction.TransactionGlucoseValue( return CgmSourceTransaction.TransactionGlucoseValue(
timestamp = sgv.mills, timestamp = sgv.mills ?: return null,
value = sgv.mgdl.toDouble(), value = sgv.mgdl?.toDouble() ?: return null,
noise = null, noise = null,
raw = if (sgv.filtered != null) sgv.filtered.toDouble() else sgv.mgdl.toDouble(), raw = sgv.filtered?.toDouble() ?: sgv.mgdl?.toDouble(),
trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction), trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction),
nightscoutId = sgv.id, nightscoutId = sgv.id,
sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device) sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device)
) )
} }
@Suppress("SpellCheckingInspection")
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(R.string.key_ns_autobackfill, true) && !dexcomPlugin.isEnabled()) return Result.failure() if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(R.string.key_ns_autobackfill, true) && !dexcomPlugin.isEnabled()) return Result.failure()
val sgvs = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure()
try { try {
var latestDateInReceivedData: Long = 0
aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvs")
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>() val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
inputData.getString("sgv")?.let { sgvString -> for (i in 0 until sgvs.length()) {
aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvString") val sgv = toGv(sgvs.getJSONObject(i)) ?: continue
glucoseValues += toGv(JSONObject(sgvString)) if (sgv.timestamp < dateUtil._now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp
glucoseValues += sgv
} }
inputData.getString("sgvs")?.let { sgvString -> // Was that sgv more less 5 mins ago ?
aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvString") if (T.msecs(dateUtil._now() - latestDateInReceivedData).mins() < 5L) {
val jsonArray = JSONArray(sgvString) rxBus.send(EventDismissNotification(Notification.NS_ALARM))
for (i in 0 until jsonArray.length()) rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
glucoseValues += toGv(jsonArray.getJSONObject(i))
} }
nsClientSourcePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null, !nsClientSourcePlugin.isEnabled())).subscribe({ result ->
result.updated.forEach { nsClientPlugin.updateLatestDateReceivedIfNewer(latestDateInReceivedData)
//aapsLogger.debug("XXXXX: Updated $it")
broadcastToXDrip(it) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null, !nsClientSourcePlugin.isEnabled()))
nsClientSourcePlugin.detectSource(it) .doOnError {
aapsLogger.error("Error while saving values from NSClient App", it)
ret = Result.failure()
} }
result.inserted.forEach { .blockingGet()
//aapsLogger.debug("XXXXX: Inserted $it") .also { result ->
broadcastToXDrip(it) result.updated.forEach {
nsClientSourcePlugin.detectSource(it) broadcastToXDrip(it)
nsClientSourcePlugin.detectSource(it)
aapsLogger.debug(LTag.BGSOURCE, "Updated bg $it")
}
result.inserted.forEach {
broadcastToXDrip(it)
nsClientSourcePlugin.detectSource(it)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
}
} }
}, {
aapsLogger.error("Error while saving values from NSClient App", it)
})
} catch (e: Exception) { } catch (e: Exception) {
aapsLogger.error("Unhandled exception", e) aapsLogger.error("Unhandled exception", e)
return Result.failure() ret = Result.failure()
} }
// Objectives 0 // Objectives 0
sp.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true) sp.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true)
return Result.success() return ret
} }
} }
} }

View file

@ -20,8 +20,6 @@ import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
import javax.inject.Inject import javax.inject.Inject
@ -42,13 +40,6 @@ class PoctechPlugin @Inject constructor(
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
), BgSourceInterface { ), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection // cannot be inner class because of needed injection
class PoctechWorker( class PoctechWorker(
context: Context, context: Context,
@ -68,6 +59,8 @@ class PoctechPlugin @Inject constructor(
} }
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!poctechPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() if (!poctechPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
aapsLogger.debug(LTag.BGSOURCE, "Received Poctech Data $inputData") aapsLogger.debug(LTag.BGSOURCE, "Received Poctech Data $inputData")
try { try {
@ -86,20 +79,25 @@ class PoctechPlugin @Inject constructor(
sourceSensor = GlucoseValue.SourceSensor.POCTECH_NATIVE sourceSensor = GlucoseValue.SourceSensor.POCTECH_NATIVE
) )
} }
poctechPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
savedValues.inserted.forEach { .doOnError {
broadcastToXDrip(it) aapsLogger.error("Error while saving values from Poctech App", it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) ret = Result.failure()
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.POCTECH_NATIVE.text) }
.blockingGet()
.also { savedValues ->
savedValues.inserted.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.POCTECH_NATIVE.text)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
}
} }
}, {
aapsLogger.error("Error while saving values from Poctech App", it)
})
} catch (e: JSONException) { } catch (e: JSONException) {
aapsLogger.error("Exception: ", e) aapsLogger.error("Exception: ", e)
return Result.failure() ret = Result.failure()
} }
return Result.success() return ret
} }
} }
} }

View file

@ -112,6 +112,7 @@ class RandomBgPlugin @Inject constructor(
xDripBroadcast(it) xDripBroadcast(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.RANDOM.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.RANDOM.text)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
} }
}, { }, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Random plugin", it) aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Random plugin", it)

View file

@ -18,8 +18,6 @@ import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -39,13 +37,6 @@ class TomatoPlugin @Inject constructor(
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
), BgSourceInterface { ), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection // cannot be inner class because of needed injection
class TomatoWorker( class TomatoWorker(
context: Context, context: Context,
@ -64,7 +55,10 @@ class TomatoPlugin @Inject constructor(
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
} }
@Suppress("SpellCheckingInspection")
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!tomatoPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() if (!tomatoPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>() val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue( glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
@ -75,16 +69,21 @@ class TomatoPlugin @Inject constructor(
trendArrow = GlucoseValue.TrendArrow.NONE, trendArrow = GlucoseValue.TrendArrow.NONE,
sourceSensor = GlucoseValue.SourceSensor.LIBRE_1_TOMATO sourceSensor = GlucoseValue.SourceSensor.LIBRE_1_TOMATO
) )
tomatoPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
savedValues.inserted.forEach { .doOnError {
broadcastToXDrip(it) aapsLogger.error("Error while saving values from Tomato App", it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) ret = Result.failure()
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.LIBRE_1_TOMATO.text)
} }
}, { .blockingGet()
aapsLogger.error("Error while saving values from Tomato App", it) .also { savedValues ->
}) savedValues.inserted.forEach {
return Result.success() broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.LIBRE_1_TOMATO.text)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
}
}
return ret
} }
} }
} }

View file

@ -14,12 +14,9 @@ import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.receivers.BundleStore import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.receivers.DataReceiver
import info.nightscout.androidaps.services.Intents import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -55,33 +52,29 @@ class XdripPlugin @Inject constructor(
).any { it == glucoseValue.sourceSensor } ).any { it == glucoseValue.sourceSensor }
} }
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection // cannot be inner class because of needed injection
class XdripWorker( class XdripWorker(
context: Context, context: Context,
params: WorkerParameters params: WorkerParameters
) : Worker(context, params) { ) : Worker(context, params) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var xdripPlugin: XdripPlugin @Inject lateinit var xdripPlugin: XdripPlugin
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
init { init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
} }
override fun doWork(): Result { override fun doWork(): Result {
var ret = Result.success()
if (!xdripPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() if (!xdripPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
val bundle = bundleStore.pickup(inputData.getLong(DataReceiver.STORE_KEY, -1)) val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1))
?: return Result.failure() ?: return Result.failure()
xdripPlugin.aapsLogger.debug(LTag.BGSOURCE, "Received xDrip data: $bundle") aapsLogger.debug(LTag.BGSOURCE, "Received xDrip data: $bundle")
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>() val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue( glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = bundle.getLong(Intents.EXTRA_TIMESTAMP, 0), timestamp = bundle.getLong(Intents.EXTRA_TIMESTAMP, 0),
@ -92,15 +85,20 @@ class XdripPlugin @Inject constructor(
sourceSensor = GlucoseValue.SourceSensor.fromString(bundle.getString(Intents.XDRIP_DATA_SOURCE_DESCRIPTION) sourceSensor = GlucoseValue.SourceSensor.fromString(bundle.getString(Intents.XDRIP_DATA_SOURCE_DESCRIPTION)
?: "") ?: "")
) )
xdripPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
savedValues.all().forEach { .doOnError {
xdripPlugin.detectSource(it) aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it)
ret = Result.failure()
}
.blockingGet()
.also { savedValues ->
savedValues.all().forEach {
xdripPlugin.detectSource(it)
aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it")
}
} }
}, {
xdripPlugin.aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it)
})
xdripPlugin.sensorBatteryLevel = bundle.getInt(Intents.EXTRA_SENSOR_BATTERY, -1) xdripPlugin.sensorBatteryLevel = bundle.getInt(Intents.EXTRA_SENSOR_BATTERY, -1)
return Result.success() return ret
} }
} }
} }

View file

@ -1,24 +0,0 @@
package info.nightscout.androidaps.receivers
import android.os.Bundle
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BundleStore @Inject constructor() {
private val store = HashMap<Long, Bundle>()
private var counter = 0L
@Synchronized
fun store(bundle: Bundle) : Long {
store.put(counter, bundle)
return counter++
}
@Synchronized
fun pickup(key: Long) : Bundle? {
val bundle = store[key]
store.remove(key)
return bundle
}
}

View file

@ -2,34 +2,26 @@ package info.nightscout.androidaps.receivers
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle
import android.provider.Telephony import android.provider.Telephony
import androidx.work.Data import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import dagger.android.DaggerBroadcastReceiver import dagger.android.DaggerBroadcastReceiver
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.BundleLogger import info.nightscout.androidaps.logging.BundleLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.source.* import info.nightscout.androidaps.plugins.source.*
import info.nightscout.androidaps.services.Intents import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.extensions.copyDouble import info.nightscout.androidaps.utils.extensions.copyDouble
import info.nightscout.androidaps.utils.extensions.copyInt import info.nightscout.androidaps.utils.extensions.copyInt
import info.nightscout.androidaps.utils.extensions.copyLong import info.nightscout.androidaps.utils.extensions.copyLong
import info.nightscout.androidaps.utils.extensions.copyString import info.nightscout.androidaps.utils.extensions.copyString
import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
open class DataReceiver : DaggerBroadcastReceiver() { open class DataReceiver : DaggerBroadcastReceiver() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var bundleStore: BundleStore @Inject lateinit var dataWorker: DataWorker
private val jobGroupName = "data"
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent) super.onReceive(context, intent)
@ -38,37 +30,29 @@ open class DataReceiver : DaggerBroadcastReceiver() {
when (intent.action) { when (intent.action) {
Intents.ACTION_NEW_BG_ESTIMATE -> Intents.ACTION_NEW_BG_ESTIMATE ->
OneTimeWorkRequest.Builder(XdripPlugin.XdripWorker::class.java) OneTimeWorkRequest.Builder(XdripPlugin.XdripWorker::class.java)
.setInputData(bundleInputData(bundle, intent)).build() .setInputData(dataWorker.storeInputData(bundle, intent)).build()
Intents.POCTECH_BG -> Intents.POCTECH_BG ->
OneTimeWorkRequest.Builder(PoctechPlugin.PoctechWorker::class.java) OneTimeWorkRequest.Builder(PoctechPlugin.PoctechWorker::class.java)
.setInputData(Data.Builder().also { .setInputData(Data.Builder().also {
it.copyString("data", bundle) it.copyString("data", bundle)
}.build()).build() }.build()).build()
Intents.GLIMP_BG -> Intents.GLIMP_BG ->
OneTimeWorkRequest.Builder(GlimpPlugin.GlimpWorker::class.java) OneTimeWorkRequest.Builder(GlimpPlugin.GlimpWorker::class.java)
.setInputData(Data.Builder().also { .setInputData(Data.Builder().also {
it.copyDouble("mySGV", bundle) it.copyDouble("mySGV", bundle)
it.copyString("myTrend", bundle) it.copyString("myTrend", bundle)
it.copyLong("myTimestamp", bundle) it.copyLong("myTimestamp", bundle)
}.build()).build() }.build()).build()
Intents.TOMATO_BG -> Intents.TOMATO_BG ->
@Suppress("SpellCheckingInspection")
OneTimeWorkRequest.Builder(TomatoPlugin.TomatoWorker::class.java) OneTimeWorkRequest.Builder(TomatoPlugin.TomatoWorker::class.java)
.setInputData(Data.Builder().also { .setInputData(Data.Builder().also {
it.copyDouble("com.fanqies.tomatofn.Extras.BgEstimate", bundle) it.copyDouble("com.fanqies.tomatofn.Extras.BgEstimate", bundle)
it.copyLong("com.fanqies.tomatofn.Extras.Time", bundle) it.copyLong("com.fanqies.tomatofn.Extras.Time", bundle)
}.build()).build() }.build()).build()
Intents.ACTION_NEW_PROFILE -> Intents.NS_EMULATOR ->
OneTimeWorkRequest.Builder(NSProfilePlugin.NSProfileWorker::class.java)
.setInputData(bundleInputData(bundle, intent)).build()
Intents.ACTION_NEW_SGV ->
OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker::class.java)
.setInputData(Data.Builder().also {
it.copyString("sgv", bundle, null)
it.copyString("sgvs", bundle, null)
}.build()).build()
Intents.NS_EMULATOR ->
OneTimeWorkRequest.Builder(MM640gPlugin.MM640gWorker::class.java) OneTimeWorkRequest.Builder(MM640gPlugin.MM640gWorker::class.java)
.setInputData(Data.Builder().also { .setInputData(Data.Builder().also {
it.copyDouble(Intents.EXTRA_BG_ESTIMATE, bundle) it.copyDouble(Intents.EXTRA_BG_ESTIMATE, bundle)
@ -80,32 +64,15 @@ open class DataReceiver : DaggerBroadcastReceiver() {
}.build()).build() }.build()).build()
Telephony.Sms.Intents.SMS_RECEIVED_ACTION -> Telephony.Sms.Intents.SMS_RECEIVED_ACTION ->
OneTimeWorkRequest.Builder(SmsCommunicatorPlugin.SmsCommunicatorWorker::class.java) OneTimeWorkRequest.Builder(SmsCommunicatorPlugin.SmsCommunicatorWorker::class.java)
.setInputData(bundleInputData(bundle, intent)).build() .setInputData(dataWorker.storeInputData(bundle, intent)).build()
Intents.EVERSENSE_BG -> Intents.EVERSENSE_BG ->
OneTimeWorkRequest.Builder(EversensePlugin.EversenseWorker::class.java) OneTimeWorkRequest.Builder(EversensePlugin.EversenseWorker::class.java)
.setInputData(bundleInputData(bundle, intent)).build() .setInputData(dataWorker.storeInputData(bundle, intent)).build()
Intents.DEXCOM_BG -> Intents.DEXCOM_BG ->
OneTimeWorkRequest.Builder(DexcomPlugin.DexcomWorker::class.java) OneTimeWorkRequest.Builder(DexcomPlugin.DexcomWorker::class.java)
.setInputData(bundleInputData(bundle, intent)).build() .setInputData(dataWorker.storeInputData(bundle, intent)).build()
Intents.ACTION_NEW_TREATMENT,
Intents.ACTION_CHANGED_TREATMENT,
Intents.ACTION_REMOVED_TREATMENT,
Intents.ACTION_NEW_CAL,
Intents.ACTION_NEW_MBG ->
OneTimeWorkRequest.Builder(NSClientWorker::class.java)
.setInputData(bundleInputData(bundle, intent)).build()
else -> null else -> null
}?.let { request -> }?.let { request -> dataWorker.enqueue(request) }
WorkManager.getInstance(context)
.enqueueUniqueWork(jobGroupName, ExistingWorkPolicy.APPEND_OR_REPLACE , request)
}
} }
private fun bundleInputData(bundle: Bundle, intent: Intent) =
Data.Builder().putLong(STORE_KEY, bundleStore.store(bundle)).putString(ACTION_KEY, intent.action).build()
companion object {
const val STORE_KEY = "storeKey"
const val ACTION_KEY = "action"
}
} }

View file

@ -0,0 +1,70 @@
package info.nightscout.androidaps.receivers
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import org.json.JSONArray
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DataWorker @Inject constructor(
private val context: Context
) {
private val store = HashMap<Long, Any>()
private var counter = 0L
private val jobGroupName = "data"
@Synchronized private fun store(value: Any): Long {
store[counter] = value
return counter++
}
@Synchronized fun pickupBundle(key: Long): Bundle? {
val value = store[key]
store.remove(key)
return value as Bundle?
}
@Synchronized fun pickupString(key: Long): String? {
val value = store[key]
store.remove(key)
return value as String?
}
@Synchronized fun pickupJSONArray(key: Long): JSONArray? {
val value = store[key]
store.remove(key)
return value as JSONArray?
}
@Synchronized fun pickupJSONObject(key: Long): JSONObject? {
val value = store[key]
store.remove(key)
return value as JSONObject?
}
fun storeInputData(value: Any, intent: Intent? = null) =
Data.Builder()
.putLong(STORE_KEY, store(value))
.putString(ACTION_KEY, intent?.action).build()
fun enqueue(request: OneTimeWorkRequest) {
WorkManager.getInstance(context)
.enqueueUniqueWork(jobGroupName, ExistingWorkPolicy.APPEND_OR_REPLACE, request)
}
companion object {
const val STORE_KEY = "storeKey"
const val ACTION_KEY = "action"
}
}

View file

@ -5,7 +5,7 @@ interface Intents {
companion object { companion object {
// NSClient -> App // AAPS -> Xdrip
const val ACTION_NEW_TREATMENT = "info.nightscout.client.NEW_TREATMENT" const val ACTION_NEW_TREATMENT = "info.nightscout.client.NEW_TREATMENT"
const val ACTION_CHANGED_TREATMENT = "info.nightscout.client.CHANGED_TREATMENT" const val ACTION_CHANGED_TREATMENT = "info.nightscout.client.CHANGED_TREATMENT"
const val ACTION_REMOVED_TREATMENT = "info.nightscout.client.REMOVED_TREATMENT" const val ACTION_REMOVED_TREATMENT = "info.nightscout.client.REMOVED_TREATMENT"
@ -14,7 +14,7 @@ interface Intents {
const val ACTION_NEW_MBG = "info.nightscout.client.NEW_MBG" const val ACTION_NEW_MBG = "info.nightscout.client.NEW_MBG"
const val ACTION_NEW_CAL = "info.nightscout.client.NEW_CAL" const val ACTION_NEW_CAL = "info.nightscout.client.NEW_CAL"
// xDrip -> App // xDrip -> AAPS
const val ACTION_NEW_BG_ESTIMATE = "com.eveningoutpost.dexdrip.BgEstimate" const val ACTION_NEW_BG_ESTIMATE = "com.eveningoutpost.dexdrip.BgEstimate"
const val EXTRA_BG_ESTIMATE = "com.eveningoutpost.dexdrip.Extras.BgEstimate" const val EXTRA_BG_ESTIMATE = "com.eveningoutpost.dexdrip.Extras.BgEstimate"
const val EXTRA_BG_SLOPE = "com.eveningoutpost.dexdrip.Extras.BgSlope" const val EXTRA_BG_SLOPE = "com.eveningoutpost.dexdrip.Extras.BgSlope"

View file

@ -4,7 +4,17 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context="info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsExtendedBolusesFragment"> tools:context="info.nightscout.androidaps.plugins.general.food.TreatmentsFoodFragment">
<info.nightscout.androidaps.utils.ui.SingleClickButton
android:id="@+id/refresh_from_nightscout"
style="?android:attr/buttonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:drawableStart="@drawable/ic_refresh"
android:text="@string/refresheventsfromnightscout"
tools:visibility="visible" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -48,6 +58,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="@string/category" /> android:text="@string/category" />
<Spinner <Spinner
@ -65,6 +76,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="@string/subcategory" /> android:text="@string/subcategory" />
<Spinner <Spinner

View file

@ -1,13 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
card_view:cardBackgroundColor="@color/cardColorBackground" card_view:cardBackgroundColor="?android:colorBackground">
card_view:cardCornerRadius="6dp"
card_view:cardUseCompatPadding="true"
card_view:contentPadding="6dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -26,21 +24,24 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Name" android:text="Name"
android:textStyle="bold" /> android:textStyle="bold"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/portion" android:id="@+id/portion"
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="Portion" /> android:text="Portion"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/carbs" android:id="@+id/carbs"
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="Carbs" /> android:text="Carbs"
tools:ignore="HardcodedText" />
</LinearLayout> </LinearLayout>
@ -56,21 +57,24 @@
android:layout_width="70dp" android:layout_width="70dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="Fat" /> android:text="Fat"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/protein" android:id="@+id/protein"
android:layout_width="70dp" android:layout_width="70dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="Protein" /> android:text="Protein"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/energy" android:id="@+id/energy"
android:layout_width="70dp" android:layout_width="70dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:gravity="end"
android:text="Energy" /> android:text="Energy"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/ns_sign" android:id="@+id/ns_sign"
@ -79,7 +83,8 @@
android:width="30dp" android:width="30dp"
android:text="NS" android:text="NS"
android:textAlignment="viewEnd" android:textAlignment="viewEnd"
android:textColor="@color/colorSetTempButton" /> android:textColor="@color/colorSetTempButton"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/remove" android:id="@+id/remove"
@ -93,6 +98,15 @@
</LinearLayout> </LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:background="@color/list_delimiter" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View file

@ -317,7 +317,7 @@
<string name="openapsma_autosensdata_label">Autosens data</string> <string name="openapsma_autosensdata_label">Autosens data</string>
<string name="openapsma_scriptdebugdata_label">Script debug</string> <string name="openapsma_scriptdebugdata_label">Script debug</string>
<string name="openapsama_useautosens">Use Autosens feature</string> <string name="openapsama_useautosens">Use Autosens feature</string>
<string name="refresheventsfromnightscout">Refresh events from NS</string> <string name="refresheventsfromnightscout">Refresh from NS</string>
<string name="deletefuturetreatments">Delete treatments in the future</string> <string name="deletefuturetreatments">Delete treatments in the future</string>
<string name="actions_shortname">ACT</string> <string name="actions_shortname">ACT</string>
<string name="configbuilder_shortname">CONF</string> <string name="configbuilder_shortname">CONF</string>

View file

@ -27,16 +27,9 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
@RunWith(PowerMockRunner::class)
@PrepareForTest(
ConstraintChecker::class, VirtualPumpPlugin::class, FabricPrivacy::class, ReceiverStatusStore::class,
IobCobCalculatorPlugin::class, AppRepository::class)
class LoopPluginTest : TestBase() { class LoopPluginTest : TestBase() {
@Mock lateinit var sp: SP @Mock lateinit var sp: SP

View file

@ -2,7 +2,6 @@ package info.nightscout.androidaps.interfaces
import com.j256.ormlite.dao.CloseableIterator import com.j256.ormlite.dao.CloseableIterator
import info.nightscout.androidaps.db.* import info.nightscout.androidaps.db.*
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import org.json.JSONObject import org.json.JSONObject
interface DatabaseHelperInterface { interface DatabaseHelperInterface {
@ -54,7 +53,7 @@ interface DatabaseHelperInterface {
fun deleteProfileSwitchById(_id: String) fun deleteProfileSwitchById(_id: String)
fun createTempBasalFromJsonIfNotExists(json: JSONObject) fun createTempBasalFromJsonIfNotExists(json: JSONObject)
fun createExtendedBolusFromJsonIfNotExists(json: JSONObject) fun createExtendedBolusFromJsonIfNotExists(json: JSONObject)
fun createProfileSwitchFromJsonIfNotExists(activePluginProvider: ActivePluginProvider, nsUpload: NSUpload, trJson: JSONObject) fun createProfileSwitchFromJsonIfNotExists(trJson: JSONObject)
fun getInsightBolusID(pumpSerial: String, bolusID: Int, timestamp: Long): InsightBolusID? fun getInsightBolusID(pumpSerial: String, bolusID: Int, timestamp: Long): InsightBolusID?
fun getInsightHistoryOffset(pumpSerial: String): InsightHistoryOffset? fun getInsightHistoryOffset(pumpSerial: String): InsightHistoryOffset?

View file

@ -14,5 +14,5 @@ interface ImportExportPrefsInterface {
fun prefsFileExists(): Boolean fun prefsFileExists(): Boolean
fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable) fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable)
fun exportSharedPreferences(f: Fragment) fun exportSharedPreferences(f: Fragment)
fun exportUserEntriesCsv(activity: FragmentActivity, entries: Single<List<UserEntry>>) fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single<List<UserEntry>>)
} }

View file

@ -13,7 +13,7 @@ enum class LTag(val tag: String, val defaultValue : Boolean = true, val requires
DATASERVICE("DATASERVICE"), DATASERVICE("DATASERVICE"),
DATATREATMENTS("DATATREATMENTS"), DATATREATMENTS("DATATREATMENTS"),
EVENTS("EVENTS", defaultValue = false, requiresRestart = true), EVENTS("EVENTS", defaultValue = false, requiresRestart = true),
GLUCOSE("GLUCOSE"), GLUCOSE("GLUCOSE", defaultValue = false),
LOCATION("LOCATION"), LOCATION("LOCATION"),
NOTIFICATION("NOTIFICATION"), NOTIFICATION("NOTIFICATION"),
NSCLIENT("NSCLIENT"), NSCLIENT("NSCLIENT"),

View file

@ -10,7 +10,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class ConstraintChecker @Inject constructor(private val activePlugin: ActivePluginProvider) : ConstraintsInterface { open class ConstraintChecker @Inject constructor(private val activePlugin: ActivePluginProvider) : ConstraintsInterface {
fun isLoopInvocationAllowed(): Constraint<Boolean> = fun isLoopInvocationAllowed(): Constraint<Boolean> =
isLoopInvocationAllowed(Constraint(true)) isLoopInvocationAllowed(Constraint(true))

View file

@ -1,41 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient.data;
import org.json.JSONException;
import org.json.JSONObject;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.logging.AAPSLogger;
public class NSMbg {
@Inject public AAPSLogger aapsLogger;
public long date;
public double mbg;
public String json;
public NSMbg(HasAndroidInjector injector) {
injector.androidInjector().inject(this);
}
public NSMbg(HasAndroidInjector injector, JSONObject json) {
this(injector);
try {
date = json.getLong("mills");
mbg = json.getDouble("mgdl");
this.json = json.toString();
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
aapsLogger.error("Data: " + json.toString());
}
}
public String id() {
try {
return new JSONObject(json).getString("_id");
} catch (JSONException e) {
return null;
}
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.plugins.general.nsclient.data
import info.nightscout.androidaps.utils.JsonHelper
import org.json.JSONObject
class NSMbg(val json: JSONObject) {
var date: Long = 0
var mbg: Double = 0.0
init {
date = JsonHelper.safeGetLong(json, "mills")
mbg = JsonHelper.safeGetDouble(json, "mgdl")
}
fun id(): String? = JsonHelper.safeGetStringAllowNull(json, "_id", null)
fun isValid(): Boolean = date != 0L && mbg != 0.0
}

View file

@ -1,72 +0,0 @@
package info.nightscout.androidaps.plugins.general.nsclient.data;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
/**
*
* {"mgdl":105,"mills":1455136282375,"device":"xDrip-BluetoothWixel","direction":"Flat","filtered":98272,"unfiltered":98272,"noise":1,"rssi":100}
*/
public class NSSgv {
private static final Logger log = StacktraceLoggerWrapper.getLogger(LTag.NSCLIENT);
private final JSONObject data;
public NSSgv(JSONObject obj) {
this.data = obj;
}
private String getStringOrNull(String key) {
String ret = null;
if (data.has(key)) {
try {
ret = data.getString(key);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
private Integer getIntegerOrNull(String key) {
Integer ret = null;
if (data.has(key)) {
try {
ret = data.getInt(key);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
private Long getLongOrNull(String key) {
Long ret = null;
if (data.has(key)) {
try {
ret = data.getLong(key);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
}
return ret;
}
public JSONObject getData () { return data; }
public Integer getMgdl () { return getIntegerOrNull("mgdl"); }
public Integer getFiltered () { return getIntegerOrNull("filtered"); }
public Integer getUnfiltered () { return getIntegerOrNull("unfiltered"); }
public Integer getNoise () { return getIntegerOrNull("noise"); }
public Integer getRssi () { return getIntegerOrNull("rssi"); }
public Long getMills () { return getLongOrNull("mills"); }
public String getDevice () { return getStringOrNull("device"); }
public String getDirection () { return getStringOrNull("direction"); }
public String getId () { return getStringOrNull("_id"); }
}

View file

@ -9,7 +9,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class ReceiverStatusStore @Inject constructor(val context: Context, val rxBus: RxBusWrapper) { open class ReceiverStatusStore @Inject constructor(val context: Context, val rxBus: RxBusWrapper) {
var lastNetworkEvent: EventNetworkChange? = null var lastNetworkEvent: EventNetworkChange? = null

View file

@ -18,7 +18,7 @@ import javax.inject.Singleton
* emulates the methods but ignores the request if the instance is null or invalid. * emulates the methods but ignores the request if the instance is null or invalid.
*/ */
@Singleton @Singleton
class FabricPrivacy @Inject constructor( open class FabricPrivacy @Inject constructor(
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
private val sp: SP private val sp: SP
) { ) {

View file

@ -5,7 +5,6 @@ import org.json.JSONObject
object JsonHelper { object JsonHelper {
@JvmStatic
fun safeGetObject(json: JSONObject?, fieldName: String, defaultValue: Any): Any { fun safeGetObject(json: JSONObject?, fieldName: String, defaultValue: Any): Any {
var result = defaultValue var result = defaultValue
if (json != null && json.has(fieldName)) { if (json != null && json.has(fieldName)) {
@ -17,7 +16,6 @@ object JsonHelper {
return result return result
} }
@JvmStatic
fun safeGetJSONObject(json: JSONObject?, fieldName: String, defaultValue: JSONObject?): JSONObject? { fun safeGetJSONObject(json: JSONObject?, fieldName: String, defaultValue: JSONObject?): JSONObject? {
var result = defaultValue var result = defaultValue
if (json != null && json.has(fieldName)) { if (json != null && json.has(fieldName)) {
@ -77,7 +75,6 @@ object JsonHelper {
return result return result
} }
@JvmStatic
fun safeGetDoubleAllowNull(json: JSONObject?, fieldName: String): Double? { fun safeGetDoubleAllowNull(json: JSONObject?, fieldName: String): Double? {
var result: Double? = null var result: Double? = null
if (json != null && json.has(fieldName)) { if (json != null && json.has(fieldName)) {
@ -89,7 +86,6 @@ object JsonHelper {
return result return result
} }
@JvmStatic
fun safeGetDouble(json: JSONObject?, fieldName: String, defaultValue: Double): Double { fun safeGetDouble(json: JSONObject?, fieldName: String, defaultValue: Double): Double {
var result = defaultValue var result = defaultValue
if (json != null && json.has(fieldName)) { if (json != null && json.has(fieldName)) {
@ -105,7 +101,6 @@ object JsonHelper {
fun safeGetInt(json: JSONObject?, fieldName: String): Int = fun safeGetInt(json: JSONObject?, fieldName: String): Int =
safeGetInt(json, fieldName, 0) safeGetInt(json, fieldName, 0)
@JvmStatic
fun safeGetInt(json: JSONObject?, fieldName: String, defaultValue: Int): Int { fun safeGetInt(json: JSONObject?, fieldName: String, defaultValue: Int): Int {
var result = defaultValue var result = defaultValue
if (json != null && json.has(fieldName)) { if (json != null && json.has(fieldName)) {
@ -117,6 +112,17 @@ object JsonHelper {
return result return result
} }
fun safeGetIntAllowNull(json: JSONObject?, fieldName: String): Int? {
var result: Int? = null
if (json != null && json.has(fieldName)) {
try {
result = json.getInt(fieldName)
} catch (ignored: JSONException) {
}
}
return result
}
@JvmStatic @JvmStatic
fun safeGetLong(json: JSONObject?, fieldName: String): Long { fun safeGetLong(json: JSONObject?, fieldName: String): Long {
var result: Long = 0 var result: Long = 0

View file

@ -0,0 +1,40 @@
package info.nightscout.androidaps.utils.extensions
import info.nightscout.androidaps.database.entities.Food
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.JsonHelper
import org.json.JSONObject
fun foodFromJson(jsonObject: JSONObject): Food? {
if ("food" == JsonHelper.safeGetString(jsonObject, "type")) {
val name = JsonHelper.safeGetStringAllowNull(jsonObject, "name", null) ?: return null
val category = JsonHelper.safeGetStringAllowNull(jsonObject, "category", null)
val subCategory = JsonHelper.safeGetStringAllowNull(jsonObject, "subcategory", null)
val unit = JsonHelper.safeGetString(jsonObject, "unit", "")
val portion = JsonHelper.safeGetDoubleAllowNull(jsonObject, "portion") ?: return null
val carbs = JsonHelper.safeGetIntAllowNull(jsonObject, "carbs") ?: return null
val gi = JsonHelper.safeGetIntAllowNull(jsonObject, "gi")
val energy = JsonHelper.safeGetIntAllowNull(jsonObject, "energy")
val protein = JsonHelper.safeGetIntAllowNull(jsonObject, "protein")
val fat = JsonHelper.safeGetIntAllowNull(jsonObject, "fat")
val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null
val isValid = JsonHelper.safeGetBoolean(jsonObject, NSUpload.ISVALID, true)
val food = Food(
name = name,
category = category,
subCategory = subCategory,
unit = unit,
portion = portion,
carbs = carbs,
gi = gi,
energy = energy,
protein = protein,
fat = fat,
isValid = isValid
)
food.interfaceIDs.nightscoutId = id
return food
}
return null
}

File diff suppressed because it is too large Load diff

View file

@ -6,13 +6,14 @@ import androidx.room.TypeConverters
import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.daos.*
import info.nightscout.androidaps.database.entities.* import info.nightscout.androidaps.database.entities.*
const val DATABASE_VERSION = 6 const val DATABASE_VERSION = 7
@Database(version = DATABASE_VERSION, @Database(version = DATABASE_VERSION,
entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class, entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class,
EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class, EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class,
TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class, TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class,
MealLink::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class], MealLink::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class,
Food::class],
exportSchema = true) exportSchema = true)
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
internal abstract class AppDatabase : RoomDatabase() { internal abstract class AppDatabase : RoomDatabase() {
@ -53,4 +54,6 @@ internal abstract class AppDatabase : RoomDatabase() {
abstract val preferenceChangeDao: PreferenceChangeDao abstract val preferenceChangeDao: PreferenceChangeDao
abstract val foodDao: FoodDao
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.database package info.nightscout.androidaps.database
import info.nightscout.androidaps.database.entities.Food
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.TherapyEvent
@ -17,7 +18,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AppRepository @Inject internal constructor( open class AppRepository @Inject internal constructor(
internal val database: AppDatabase internal val database: AppDatabase
) { ) {
@ -170,6 +171,14 @@ class AppRepository @Inject internal constructor(
database.therapyEventDao.compatGetTherapyEventDataFromToTime(from, to) database.therapyEventDao.compatGetTherapyEventDataFromToTime(from, to)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
// FOOD
fun getFoodData(): Single<List<Food>> =
database.foodDao.getFoodData()
.subscribeOn(Schedulers.io())
fun deleteAllFoods() =
database.foodDao.deleteAllEntries()
} }
@Suppress("USELESS_CAST") @Suppress("USELESS_CAST")

View file

@ -34,4 +34,10 @@ open class DatabaseModule {
database.execSQL("CREATE TABLE userEntry (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `s` TEXT NOT NULL, `values` TEXT NOT NULL)") database.execSQL("CREATE TABLE userEntry (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `s` TEXT NOT NULL, `values` TEXT NOT NULL)")
} }
} }
private val migration6to7 = object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS foods (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `name` TEXT NOT NULL, `category` TEXT, `subCategory` TEXT, `portion` REAL NOT NULL, `carbs` INTEGER NOT NULL, `fat` INTEGER, `protein` INTEGER, `energy` INTEGER, `unit` TEXT NOT NULL, `gi` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `foods`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
}
}
} }

View file

@ -24,5 +24,6 @@ internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val datab
val versionChangeDao: VersionChangeDao = DelegatedVersionChangeDao(changes, database.versionChangeDao) val versionChangeDao: VersionChangeDao = DelegatedVersionChangeDao(changes, database.versionChangeDao)
val userEntryDao: UserEntryDao = DelegatedUserEntryDao(changes, database.userEntryDao) val userEntryDao: UserEntryDao = DelegatedUserEntryDao(changes, database.userEntryDao)
val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao) val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao)
val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao)
fun clearAllTables() = database.clearAllTables() fun clearAllTables() = database.clearAllTables()
} }

View file

@ -8,6 +8,7 @@ const val TABLE_CARBS = "carbs"
const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches" const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches"
const val TABLE_EXTENDED_BOLUSES = "extendedBoluses" const val TABLE_EXTENDED_BOLUSES = "extendedBoluses"
const val TABLE_GLUCOSE_VALUES = "glucoseValues" const val TABLE_GLUCOSE_VALUES = "glucoseValues"
const val TABLE_FOODS = "foods"
const val TABLE_MEAL_LINKS = "mealLinks" const val TABLE_MEAL_LINKS = "mealLinks"
const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks" const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks"
const val TABLE_PROFILE_SWITCHES = "profileSwitches" const val TABLE_PROFILE_SWITCHES = "profileSwitches"

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_FOODS
import info.nightscout.androidaps.database.entities.Food
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface FoodDao : TraceableDao<Food> {
@Query("SELECT * FROM $TABLE_FOODS WHERE id = :id")
override fun findById(id: Long): Food?
@Query("DELETE FROM $TABLE_FOODS")
override fun deleteAllEntries()
@Query("SELECT * FROM $TABLE_FOODS WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSId(nsId: String): Food?
@Query("SELECT * FROM $TABLE_FOODS WHERE isValid = 1 AND referenceId IS NULL ORDER BY id DESC")
fun getFoodData(): Single<List<Food>>
}

View file

@ -4,7 +4,6 @@ import androidx.room.Insert
import androidx.room.Update import androidx.room.Update
import info.nightscout.androidaps.database.daos.workaround.TraceableDaoWorkaround import info.nightscout.androidaps.database.daos.workaround.TraceableDaoWorkaround
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import io.reactivex.Single
internal interface TraceableDao<T : TraceableDBEntry> : TraceableDaoWorkaround<T> { internal interface TraceableDao<T : TraceableDBEntry> : TraceableDaoWorkaround<T> {

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.FoodDao
import info.nightscout.androidaps.database.entities.Food
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedFoodDao(changes: MutableList<DBEntry>, private val dao: FoodDao) : DelegatedDao(changes), FoodDao by dao {
override fun insertNewEntry(food: Food): Long {
changes.add(food)
return dao.insertNewEntry(food)
}
override fun updateExistingEntry(food: Food): Long {
changes.add(food)
return dao.updateExistingEntry(food)
}
}

View file

@ -0,0 +1,73 @@
package info.nightscout.androidaps.database.entities
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import info.nightscout.androidaps.database.TABLE_FOODS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
@Entity(tableName = TABLE_FOODS,
foreignKeys = [ForeignKey(
entity = Food::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId")])
data class Food(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = null,
var name: String,
var category: String? = null,
var subCategory: String? = null,
// Example:
// name="juice" portion=250 units="ml" carbs=12
// means 250ml of juice has 12g of carbs
var portion: Double, // common portion in "units"
var carbs: Int, // in grams
var fat: Int? = null, // in grams
var protein: Int? = null, // in grams
var energy: Int? = null, // in kJ
var unit: String = "g",
var gi: Int? = null // not used yet
) : TraceableDBEntry {
fun isEqual(other: Food): Boolean {
if (isValid != other.isValid) return false
if (portion != other.portion) return false
if (carbs != other.carbs) return false
if (fat != other.fat) return false
if (protein != other.protein) return false
if (energy != other.energy) return false
if (gi != other.gi) return false
if (name != other.name) return false
if (category != other.category) return false
if (subCategory != other.subCategory) return false
return unit == other.unit
}
fun copyFrom(other: Food) {
isValid = other.isValid
name = other.name
category = other.category
subCategory = other.subCategory
portion = other.portion
carbs = other.carbs
fat = other.fat
protein = other.protein
energy = other.energy
unit = other.unit
gi = other.gi
interfaceIDs.nightscoutId = other.interfaceIDs.nightscoutId
}
}

View file

@ -103,6 +103,7 @@ data class UserEntry(
@SerializedName("TT_DELETED_FROM_NS") TT_DELETED_FROM_NS (ColorGroup.TT), @SerializedName("TT_DELETED_FROM_NS") TT_DELETED_FROM_NS (ColorGroup.TT),
@SerializedName("CAREPORTAL_DELETED_FROM_NS") CAREPORTAL_DELETED_FROM_NS (ColorGroup.Careportal), @SerializedName("CAREPORTAL_DELETED_FROM_NS") CAREPORTAL_DELETED_FROM_NS (ColorGroup.Careportal),
@SerializedName("CAREPORTAL_FROM_NS") CAREPORTAL_FROM_NS (ColorGroup.Careportal), @SerializedName("CAREPORTAL_FROM_NS") CAREPORTAL_FROM_NS (ColorGroup.Careportal),
@SerializedName("FOOD_FROM_NS") FOOD_FROM_NS (ColorGroup.Careportal),
@SerializedName("TT_FROM_NS") TT_FROM_NS (ColorGroup.TT), @SerializedName("TT_FROM_NS") TT_FROM_NS (ColorGroup.TT),
@SerializedName("TT_CANCELED_FROM_NS") TT_CANCELED_FROM_NS (ColorGroup.TT), @SerializedName("TT_CANCELED_FROM_NS") TT_CANCELED_FROM_NS (ColorGroup.TT),
@SerializedName("EXPORT_CSV") EXPORT_CSV (ColorGroup.Aaps), @SerializedName("EXPORT_CSV") EXPORT_CSV (ColorGroup.Aaps),

View file

@ -54,21 +54,25 @@ class CgmSourceTransaction(
} }
calibrations.forEach { calibrations.forEach {
if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.FINGER_STICK_BG_VALUE, it.timestamp) == null) { if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.FINGER_STICK_BG_VALUE, it.timestamp) == null) {
database.therapyEventDao.insertNewEntry(TherapyEvent( val therapyEvent = TherapyEvent(
timestamp = it.timestamp, timestamp = it.timestamp,
type = TherapyEvent.Type.FINGER_STICK_BG_VALUE, type = TherapyEvent.Type.FINGER_STICK_BG_VALUE,
glucose = it.value, glucose = it.value,
glucoseUnit = it.glucoseUnit glucoseUnit = it.glucoseUnit
)) )
database.therapyEventDao.insertNewEntry(therapyEvent)
result.calibrationsInserted.add(therapyEvent)
} }
} }
sensorInsertionTime?.let { sensorInsertionTime?.let {
if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.SENSOR_CHANGE, it) == null) { if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.SENSOR_CHANGE, it) == null) {
database.therapyEventDao.insertNewEntry(TherapyEvent( val therapyEvent = TherapyEvent(
timestamp = it, timestamp = it,
type = TherapyEvent.Type.SENSOR_CHANGE, type = TherapyEvent.Type.SENSOR_CHANGE,
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)) )
database.therapyEventDao.insertNewEntry(therapyEvent)
result.sensorInsertionsInserted.add(therapyEvent)
} }
} }
return result return result
@ -95,6 +99,9 @@ class CgmSourceTransaction(
val inserted = mutableListOf<GlucoseValue>() val inserted = mutableListOf<GlucoseValue>()
val updated = mutableListOf<GlucoseValue>() val updated = mutableListOf<GlucoseValue>()
val calibrationsInserted = mutableListOf<TherapyEvent>()
val sensorInsertionsInserted = mutableListOf<TherapyEvent>()
fun all(): MutableList<GlucoseValue> = fun all(): MutableList<GlucoseValue> =
mutableListOf<GlucoseValue>().also { result -> mutableListOf<GlucoseValue>().also { result ->
result.addAll(inserted) result.addAll(inserted)

View file

@ -0,0 +1,28 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.Food
/**
* Inserts or updates the Food
*/
class InsertOrUpdateFoodTransaction(private val food: Food) : Transaction<InsertOrUpdateFoodTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val result = TransactionResult()
val current = database.foodDao.findById(food.id)
if (current == null) {
database.foodDao.insertNewEntry(food)
result.inserted.add(food)
} else {
database.foodDao.updateExistingEntry(food)
result.updated.add(food)
}
return result
}
class TransactionResult {
val inserted = mutableListOf<Food>()
val updated = mutableListOf<Food>()
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.database.transactions
class InvalidateFoodTransaction(val id: Long) : Transaction<Unit>() {
override fun run() {
val food = database.foodDao.findById(id)
?: throw IllegalArgumentException("There is no such Food with the specified ID.")
food.isValid = false
database.foodDao.updateExistingEntry(food)
}
}

View file

@ -0,0 +1,42 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.Food
/**
* Sync the TherapyEvents from NS
*/
class SyncFoodTransaction(private val food: Food) : Transaction<SyncFoodTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val result = TransactionResult()
val current: Food? =
food.interfaceIDs.nightscoutId?.let {
database.foodDao.findByNSId(it)
}
if (current != null) {
// nsId exists, update if different
if (!current.isEqual(food)) {
current.copyFrom(food)
database.foodDao.updateExistingEntry(current)
if (food.isValid && current.isValid) result.updated.add(current)
else if (!food.isValid && current.isValid) result.invalidated.add(current)
}
return result
}
// not known nsId, add
database.foodDao.insertNewEntry(food)
result.inserted.add(food)
return result
}
class TransactionResult {
val updated = mutableListOf<Food>()
val inserted = mutableListOf<Food>()
val invalidated = mutableListOf<Food>()
}
}