Merge branch 'ns15' into xdrip

This commit is contained in:
Milos Kozak 2023-02-04 11:01:10 +01:00
commit b5d03ff897
60 changed files with 1058 additions and 669 deletions

View file

@ -34,14 +34,14 @@ open class AppModule {
@PluginsListModule.PumpDriver pumpDrivers: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>, @PluginsListModule.PumpDriver pumpDrivers: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsListModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>, @PluginsListModule.NotNSClient notNsClient: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsListModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>, @PluginsListModule.APS aps: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>,
@PluginsListModule.Unfinished unfinished: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>> //@PluginsListModule.Unfinished unfinished: Lazy<Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>>
) )
: List<@JvmSuppressWildcards PluginBase> { : List<@JvmSuppressWildcards PluginBase> {
val plugins = allConfigs.toMutableMap() val plugins = allConfigs.toMutableMap()
if (config.PUMPDRIVERS) plugins += pumpDrivers.get() if (config.PUMPDRIVERS) plugins += pumpDrivers.get()
if (config.APS) plugins += aps.get() if (config.APS) plugins += aps.get()
if (!config.NSCLIENT) plugins += notNsClient.get() if (!config.NSCLIENT) plugins += notNsClient.get()
if (config.isUnfinishedMode()) plugins += unfinished.get() //if (config.isUnfinishedMode()) plugins += unfinished.get()
return plugins.toList().sortedBy { it.first }.map { it.second } return plugins.toList().sortedBy { it.first }.map { it.second }
} }

View file

@ -318,7 +318,7 @@ abstract class PluginsListModule {
abstract fun bindNSClientV3Plugin(plugin: NSClientV3Plugin): PluginBase abstract fun bindNSClientV3Plugin(plugin: NSClientV3Plugin): PluginBase
@Binds @Binds
@Unfinished @NotNSClient
@IntoMap @IntoMap
@IntKey(360) @IntKey(360)
abstract fun bindTidepoolPlugin(plugin: TidepoolPlugin): PluginBase abstract fun bindTidepoolPlugin(plugin: TidepoolPlugin): PluginBase

View file

@ -15,7 +15,7 @@ class ConfigImpl @Inject constructor(
fileListProvider: PrefFileListProvider fileListProvider: PrefFileListProvider
) : Config { ) : Config {
override val SUPPORTED_NS_VERSION = 140206 // 14.2.6 override val SUPPORTED_NS_VERSION = 150000 // 15.0.0
override val APS = BuildConfig.FLAVOR == "full" override val APS = BuildConfig.FLAVOR == "full"
override val NSCLIENT = BuildConfig.FLAVOR == "aapsclient" || BuildConfig.FLAVOR == "aapsclient2" override val NSCLIENT = BuildConfig.FLAVOR == "aapsclient" || BuildConfig.FLAVOR == "aapsclient2"
override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol" override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol"

View file

@ -1,5 +1,6 @@
package info.nightscout.sdk.mapper package info.nightscout.sdk.mapper
import com.google.gson.Gson
import com.google.gson.JsonParser import com.google.gson.JsonParser
import info.nightscout.sdk.localmodel.devicestatus.NSDeviceStatus import info.nightscout.sdk.localmodel.devicestatus.NSDeviceStatus
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
@ -8,6 +9,9 @@ import org.json.JSONObject
fun NSDeviceStatus.convertToRemoteAndBack(): NSDeviceStatus = fun NSDeviceStatus.convertToRemoteAndBack(): NSDeviceStatus =
toRemoteDeviceStatus().toNSDeviceStatus() toRemoteDeviceStatus().toNSDeviceStatus()
fun String.toNSDeviceStatus(): NSDeviceStatus =
Gson().fromJson(this, RemoteDeviceStatus::class.java).toNSDeviceStatus()
internal fun RemoteDeviceStatus.toNSDeviceStatus(): NSDeviceStatus = internal fun RemoteDeviceStatus.toNSDeviceStatus(): NSDeviceStatus =
NSDeviceStatus( NSDeviceStatus(
app = app, app = app,

View file

@ -1,5 +1,6 @@
package info.nightscout.sdk.mapper package info.nightscout.sdk.mapper
import com.google.gson.Gson
import info.nightscout.sdk.localmodel.food.NSFood import info.nightscout.sdk.localmodel.food.NSFood
import info.nightscout.sdk.remotemodel.RemoteFood import info.nightscout.sdk.remotemodel.RemoteFood
@ -12,6 +13,9 @@ import info.nightscout.sdk.remotemodel.RemoteFood
fun NSFood.convertToRemoteAndBack(): NSFood? = fun NSFood.convertToRemoteAndBack(): NSFood? =
toRemoteFood().toNSFood() toRemoteFood().toNSFood()
fun String.toNSFood(): NSFood? =
Gson().fromJson(this, RemoteFood::class.java).toNSFood()
internal fun RemoteFood.toNSFood(): NSFood? { internal fun RemoteFood.toNSFood(): NSFood? {
when (type) { when (type) {
"food" -> "food" ->

View file

@ -1,5 +1,6 @@
package info.nightscout.sdk.mapper package info.nightscout.sdk.mapper
import com.google.gson.Gson
import info.nightscout.sdk.localmodel.entry.Direction import info.nightscout.sdk.localmodel.entry.Direction
import info.nightscout.sdk.localmodel.entry.NSSgvV3 import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.sdk.localmodel.entry.NsUnits import info.nightscout.sdk.localmodel.entry.NsUnits
@ -8,6 +9,9 @@ import info.nightscout.sdk.remotemodel.RemoteEntry
fun NSSgvV3.convertToRemoteAndBack(): NSSgvV3? = fun NSSgvV3.convertToRemoteAndBack(): NSSgvV3? =
toRemoteEntry().toSgv() toRemoteEntry().toSgv()
fun String.toNSSgvV3(): NSSgvV3? =
Gson().fromJson(this, RemoteEntry::class.java).toSgv()
internal fun RemoteEntry.toSgv(): NSSgvV3? { internal fun RemoteEntry.toSgv(): NSSgvV3? {
this.sgv ?: return null this.sgv ?: return null

View file

@ -1,5 +1,6 @@
package info.nightscout.sdk.mapper package info.nightscout.sdk.mapper
import com.google.gson.Gson
import info.nightscout.sdk.localmodel.entry.NsUnits import info.nightscout.sdk.localmodel.entry.NsUnits
import info.nightscout.sdk.localmodel.treatment.EventType import info.nightscout.sdk.localmodel.treatment.EventType
import info.nightscout.sdk.localmodel.treatment.NSBolus import info.nightscout.sdk.localmodel.treatment.NSBolus
@ -26,6 +27,9 @@ import java.util.concurrent.TimeUnit
fun NSTreatment.convertToRemoteAndBack(): NSTreatment? = fun NSTreatment.convertToRemoteAndBack(): NSTreatment? =
toRemoteTreatment()?.toTreatment() toRemoteTreatment()?.toTreatment()
fun String.toNSTreatment(): NSTreatment? =
Gson().fromJson(this, RemoteTreatment::class.java).toTreatment()
internal fun RemoteTreatment.toTreatment(): NSTreatment? { internal fun RemoteTreatment.toTreatment(): NSTreatment? {
val treatmentTimestamp = timestamp() val treatmentTimestamp = timestamp()
when { when {

View file

@ -22,4 +22,15 @@ data class LastModified(
@SerializedName("foods") var foods: Long = 0, // foods collection @SerializedName("foods") var foods: Long = 0, // foods collection
@SerializedName("settings") var settings: Long = 0 // settings collection @SerializedName("settings") var settings: Long = 0 // settings collection
) )
fun set(colName: String, value: Long) {
when (colName) {
"devicestatus" -> collections.devicestatus = value
"entries" -> collections.entries = value
"profile" -> collections.profile = value
"treatments" -> collections.treatments = value
"foods" -> collections.foods = value
"settings" -> collections.settings = value
}
}
} }

View file

@ -45,7 +45,7 @@
<string name="mute5min">Mute for 5 minutes</string> <string name="mute5min">Mute for 5 minutes</string>
<string name="mute">Mute</string> <string name="mute">Mute</string>
<string name="success">Success</string> <string name="success">Success</string>
<string name="advancedsettings_title">Advanced Settings</string> <string name="advanced_settings_title">Advanced Settings</string>
<string name="extendedbolusdeliveryerror">Extended bolus delivery error</string> <string name="extendedbolusdeliveryerror">Extended bolus delivery error</string>
<string name="aps_mode_title">APS Mode</string> <string name="aps_mode_title">APS Mode</string>
<string name="extended_bolus">Extended bolus</string> <string name="extended_bolus">Extended bolus</string>

View file

@ -84,6 +84,7 @@
<string name="key_ns_receive_profile_store" translatable="false">ns_receive_profile_store</string> <string name="key_ns_receive_profile_store" translatable="false">ns_receive_profile_store</string>
<string name="key_nsclientinternal_url" translatable="false">nsclientinternal_url</string> <string name="key_nsclientinternal_url" translatable="false">nsclientinternal_url</string>
<string name="key_nsclientinternal_api_secret" translatable="false">nsclientinternal_api_secret</string> <string name="key_nsclientinternal_api_secret" translatable="false">nsclientinternal_api_secret</string>
<string name="key_ns_use_ws" translatable="false">ns_use_ws</string>
<string name="key_ns_receive_insulin" translatable="false">ns_receive_insulin</string> <string name="key_ns_receive_insulin" translatable="false">ns_receive_insulin</string>
<string name="key_ns_receive_carbs" translatable="false">ns_receive_carbs</string> <string name="key_ns_receive_carbs" translatable="false">ns_receive_carbs</string>
<string name="key_ns_receive_therapy_events" translatable="false">ns_receive_therapy_events</string> <string name="key_ns_receive_therapy_events" translatable="false">ns_receive_therapy_events</string>

View file

@ -8,6 +8,7 @@ import androidx.room.PrimaryKey
import info.nightscout.database.entities.embedments.InterfaceIDs import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.database.entities.interfaces.DBEntryWithTimeAndDuration import info.nightscout.database.entities.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.database.entities.interfaces.TraceableDBEntry import info.nightscout.database.entities.interfaces.TraceableDBEntry
import info.nightscout.database.entities.interfaces.end
import java.util.TimeZone import java.util.TimeZone
@Entity( @Entity(
@ -53,9 +54,6 @@ data class OfflineEvent(
previous.interfaceIDs.nightscoutId == null && previous.interfaceIDs.nightscoutId == null &&
interfaceIDs.nightscoutId != null interfaceIDs.nightscoutId != null
fun isRecordDeleted(other: OfflineEvent): Boolean =
isValid && !other.isValid
enum class Reason { enum class Reason {
DISCONNECT_PUMP, DISCONNECT_PUMP,
SUSPEND, SUSPEND,

View file

@ -52,7 +52,7 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="absorption_ama_advanced" android:key="absorption_ama_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<Preference android:summary="@string/openapsama_link_to_preference_json_doc_txt"> <Preference android:summary="@string/openapsama_link_to_preference_json_doc_txt">
<intent <intent

View file

@ -162,7 +162,7 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="absorption_smb_advanced" android:key="absorption_smb_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<Preference android:summary="@string/openapsama_link_to_preference_json_doc_txt"> <Preference android:summary="@string/openapsama_link_to_preference_json_doc_txt">
<intent <intent

View file

@ -183,7 +183,7 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="absorption_smb_advanced" android:key="absorption_smb_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<Preference android:summary="@string/openapsama_link_to_preference_json_doc_txt"> <Preference android:summary="@string/openapsama_link_to_preference_json_doc_txt">
<intent <intent

View file

@ -75,7 +75,7 @@ class RunningConfigurationImpl @Inject constructor(
assert(config.NSCLIENT) assert(config.NSCLIENT)
configuration.version?.let { configuration.version?.let {
rxBus.send(EventNSClientNewLog("VERSION", "Received AAPS version $it")) rxBus.send(EventNSClientNewLog("VERSION", "Received AAPS version $it"))
if (config.VERSION_NAME.startsWith(it).not()) if (config.VERSION_NAME.startsWith(it).not())
uiInteraction.addNotification(Notification.NSCLIENT_VERSION_DOES_NOT_MATCH, rh.gs(R.string.nsclient_version_does_not_match), Notification.NORMAL) uiInteraction.addNotification(Notification.NSCLIENT_VERSION_DOES_NOT_MATCH, rh.gs(R.string.nsclient_version_does_not_match), Notification.NORMAL)
} }

View file

@ -4,7 +4,6 @@ import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.notifications.Notification import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.plugins.R import info.nightscout.plugins.R
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
@ -21,7 +20,6 @@ class NotificationWithAction constructor(
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rh: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
init { init {
@ -66,7 +64,7 @@ class NotificationWithAction constructor(
aapsLogger.debug(LTag.NOTIFICATION, "Notification text is: $text") aapsLogger.debug(LTag.NOTIFICATION, "Notification text is: $text")
val msToSnooze = sp.getInt(info.nightscout.core.utils.R.string.key_ns_alarm_stale_data_value, 15) * 60 * 1000L val msToSnooze = sp.getInt(info.nightscout.core.utils.R.string.key_ns_alarm_stale_data_value, 15) * 60 * 1000L
aapsLogger.debug(LTag.NOTIFICATION, "snooze nsalarm_staledatavalue in minutes is ${T.msecs(msToSnooze).mins()} currentTimeMillis is: ${System.currentTimeMillis()}") aapsLogger.debug(LTag.NOTIFICATION, "snooze nsalarm_staledatavalue in minutes is ${T.msecs(msToSnooze).mins()} currentTimeMillis is: ${System.currentTimeMillis()}")
sp.putLong(info.nightscout.core.utils.R.string.key_snoozed_to, System.currentTimeMillis() + msToSnooze) sp.putLong(rh.gs(info.nightscout.core.utils.R.string.key_snoozed_to) + nsAlarm.level(), System.currentTimeMillis() + msToSnooze)
} }
} }

View file

@ -496,7 +496,7 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="overview_advanced" android:key="overview_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="false"

View file

@ -38,7 +38,7 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="absorption_aaps_advanced" android:key="absorption_aaps_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<info.nightscout.core.validators.ValidatingEditTextPreference <info.nightscout.core.validators.ValidatingEditTextPreference
android:defaultValue="1.2" android:defaultValue="1.2"

View file

@ -37,7 +37,7 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="absorption_oref1_advanced" android:key="absorption_oref1_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<info.nightscout.core.validators.ValidatingEditTextPreference <info.nightscout.core.validators.ValidatingEditTextPreference
android:defaultValue="1.2" android:defaultValue="1.2"

View file

@ -32,10 +32,7 @@ dependencies {
testImplementation project(':plugins:aps') testImplementation project(':plugins:aps')
// NSClient, Tidepool // NSClient, Tidepool
api("io.socket:socket.io-client:1.0.2") { api("io.socket:socket.io-client:2.1.0")
// excluding org.json which is provided by Android
exclude group: "org.json", module: "json"
}
api "com.squareup.okhttp3:okhttp:$okhttp3_version" api "com.squareup.okhttp3:okhttp:$okhttp3_version"
api "com.squareup.okhttp3:logging-interceptor:$okhttp3_version" api "com.squareup.okhttp3:logging-interceptor:$okhttp3_version"
//api "com.squareup.retrofit2:retrofit:$retrofit2_version" //api "com.squareup.retrofit2:retrofit:$retrofit2_version"

View file

@ -62,6 +62,7 @@ abstract class SyncModule {
@ContributesAndroidInjector abstract fun contributesLoadProfileStoreWorker(): LoadProfileStoreWorker @ContributesAndroidInjector abstract fun contributesLoadProfileStoreWorker(): LoadProfileStoreWorker
@ContributesAndroidInjector abstract fun contributesStoreBgWorker(): StoreDataForDbImpl.StoreBgWorker @ContributesAndroidInjector abstract fun contributesStoreBgWorker(): StoreDataForDbImpl.StoreBgWorker
@ContributesAndroidInjector abstract fun contributesStoreFoodWorker(): StoreDataForDbImpl.StoreFoodWorker @ContributesAndroidInjector abstract fun contributesStoreFoodWorker(): StoreDataForDbImpl.StoreFoodWorker
@ContributesAndroidInjector abstract fun contributesStoreTreatmentsWorker(): StoreDataForDbImpl.StoreTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesTreatmentWorker(): LoadTreatmentsWorker @ContributesAndroidInjector abstract fun contributesTreatmentWorker(): LoadTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesProcessTreatmentsWorker(): ProcessTreatmentsWorker @ContributesAndroidInjector abstract fun contributesProcessTreatmentsWorker(): ProcessTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesLoadDeviceStatusWorker(): LoadDeviceStatusWorker @ContributesAndroidInjector abstract fun contributesLoadDeviceStatusWorker(): LoadDeviceStatusWorker

View file

@ -57,7 +57,7 @@ class DataSyncSelectorImplementation @Inject constructor(
} }
private val queueCounter = QueueCounter() private val queueCounter = QueueCounter()
private val isPaused get() = sp.getBoolean(R.string.key_ns_client_paused, false) private val isPaused get() = sp.getBoolean(R.string.key_ns_paused, false)
override fun queueSize(): Long = queueCounter.size() override fun queueSize(): Long = queueCounter.size()

View file

@ -81,7 +81,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
updateGui() updateGui()
} }
binding.paused.isChecked = sp.getBoolean(R.string.key_ns_client_paused, false) binding.paused.isChecked = sp.getBoolean(R.string.key_ns_paused, false)
binding.paused.setOnCheckedChangeListener { _, isChecked -> binding.paused.setOnCheckedChangeListener { _, isChecked ->
uel.log(if (isChecked) UserEntry.Action.NS_PAUSED else UserEntry.Action.NS_RESUME, UserEntry.Sources.NSClient) uel.log(if (isChecked) UserEntry.Action.NS_PAUSED else UserEntry.Action.NS_RESUME, UserEntry.Sources.NSClient)
nsClientPlugin?.pause(isChecked) nsClientPlugin?.pause(isChecked)
@ -143,7 +143,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
private fun updateGui() { private fun updateGui() {
if (_binding == null) return if (_binding == null) return
binding.paused.isChecked = sp.getBoolean(R.string.key_ns_client_paused, false) binding.paused.isChecked = sp.getBoolean(R.string.key_ns_paused, false)
binding.log.text = nsClientPlugin?.textLog() binding.log.text = nsClientPlugin?.textLog()
if (sp.getBoolean(R.string.key_ns_client_autoscroll, true)) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN) if (sp.getBoolean(R.string.key_ns_client_autoscroll, true)) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN)
binding.url.text = nsClientPlugin?.address binding.url.text = nsClientPlugin?.address

View file

@ -145,13 +145,24 @@ class StoreDataForDbImpl @Inject constructor(
} }
} }
class StoreTreatmentsWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.Default) {
@Inject lateinit var storeDataForDb: StoreDataForDb
override suspend fun doWorkAndLog(): Result {
storeDataForDb.storeTreatmentsToDb()
return Result.success()
}
}
fun <T> HashMap<T, Long>.inc(key: T) = fun <T> HashMap<T, Long>.inc(key: T) =
if (containsKey(key)) merge(key, 1, Long::plus) if (containsKey(key)) merge(key, 1, Long::plus)
else put(key, 1) else put(key, 1)
override fun storeGlucoseValuesToDb() { override fun storeGlucoseValuesToDb() {
rxBus.send(EventNSClientNewLog("PROCESSING BG", ""))
if (glucoseValues.isNotEmpty()) if (glucoseValues.isNotEmpty())
repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
.doOnError { .doOnError {
@ -179,12 +190,10 @@ class StoreDataForDbImpl @Inject constructor(
sendLog("GlucoseValue", GlucoseValue::class.java.simpleName) sendLog("GlucoseValue", GlucoseValue::class.java.simpleName)
SystemClock.sleep(pause) SystemClock.sleep(pause)
rxBus.send(EventNSClientNewLog("DONE BG", "")) rxBus.send(EventNSClientNewLog("DONE PROCESSING BG", ""))
} }
override fun storeFoodsToDb() { override fun storeFoodsToDb() {
rxBus.send(EventNSClientNewLog("PROCESSING FOOD", ""))
if (foods.isNotEmpty()) if (foods.isNotEmpty())
repository.runTransactionForResult(SyncNsFoodTransaction(foods)) repository.runTransactionForResult(SyncNsFoodTransaction(foods))
.doOnError { .doOnError {
@ -209,12 +218,10 @@ class StoreDataForDbImpl @Inject constructor(
sendLog("Food", Food::class.java.simpleName) sendLog("Food", Food::class.java.simpleName)
SystemClock.sleep(pause) SystemClock.sleep(pause)
rxBus.send(EventNSClientNewLog("DONE FOOD", "")) rxBus.send(EventNSClientNewLog("DONE PROCESSING FOOD", ""))
} }
override fun storeTreatmentsToDb() { override fun storeTreatmentsToDb() {
rxBus.send(EventNSClientNewLog("PROCESSING TR", ""))
if (boluses.isNotEmpty()) if (boluses.isNotEmpty())
repository.runTransactionForResult(SyncNsBolusTransaction(boluses)) repository.runTransactionForResult(SyncNsBolusTransaction(boluses))
.doOnError { .doOnError {
@ -791,7 +798,7 @@ class StoreDataForDbImpl @Inject constructor(
SystemClock.sleep(pause) SystemClock.sleep(pause)
uel.log(userEntries) uel.log(userEntries)
rxBus.send(EventNSClientNewLog("DONE TR", "")) rxBus.send(EventNSClientNewLog("DONE PROCESSING TR", ""))
} }
private val eventWorker = Executors.newSingleThreadScheduledExecutor() private val eventWorker = Executors.newSingleThreadScheduledExecutor()
@ -991,7 +998,7 @@ class StoreDataForDbImpl @Inject constructor(
sendLog("TherapyEvent", TherapyEvent::class.java.simpleName) sendLog("TherapyEvent", TherapyEvent::class.java.simpleName)
sendLog("OfflineEvent", OfflineEvent::class.java.simpleName) sendLog("OfflineEvent", OfflineEvent::class.java.simpleName)
sendLog("ExtendedBolus", ExtendedBolus::class.java.simpleName) sendLog("ExtendedBolus", ExtendedBolus::class.java.simpleName)
rxBus.send(EventNSClientNewLog("DONE NSIDs", "")) rxBus.send(EventNSClientNewLog("DONE NSIDs", ""))
} }
private fun sendLog(item: String, clazz: String) { private fun sendLog(item: String, clazz: String) {

View file

@ -0,0 +1,5 @@
package info.nightscout.plugins.sync.nsShared.events
import info.nightscout.rx.events.Event
class EventConnectivityOptionChanged(val blockingReason: String) : Event()

View file

@ -14,7 +14,6 @@ import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.toJson import info.nightscout.core.extensions.toJson
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.validators.ValidatingEditTextPreference
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.nsclient.NSAlarm import info.nightscout.interfaces.nsclient.NSAlarm
@ -41,9 +40,7 @@ import info.nightscout.plugins.sync.nsclient.services.NSClientService
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventChargingState
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNetworkChange
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventSWSyncStatus import info.nightscout.rx.events.EventSWSyncStatus
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
@ -66,7 +63,7 @@ class NSClientPlugin @Inject constructor(
private val context: Context, private val context: Context,
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val sp: SP, private val sp: SP,
private val nsClientReceiverDelegate: NsClientReceiverDelegate, private val receiverDelegate: ReceiverDelegate,
private val config: Config, private val config: Config,
private val dataSyncSelector: DataSyncSelector, private val dataSyncSelector: DataSyncSelector,
private val uiInteraction: UiInteraction, private val uiInteraction: UiInteraction,
@ -92,14 +89,14 @@ class NSClientPlugin @Inject constructor(
override var status = "" override var status = ""
var nsClientService: NSClientService? = null var nsClientService: NSClientService? = null
val isAllowed: Boolean val isAllowed: Boolean
get() = nsClientReceiverDelegate.allowed get() = receiverDelegate.allowed
val blockingReason: String val blockingReason: String
get() = nsClientReceiverDelegate.blockingReason get() = receiverDelegate.blockingReason
override fun onStart() { override fun onStart() {
context.bindService(Intent(context, NSClientService::class.java), mConnection, Context.BIND_AUTO_CREATE) context.bindService(Intent(context, NSClientService::class.java), mConnection, Context.BIND_AUTO_CREATE)
super.onStart() super.onStart()
nsClientReceiverDelegate.grabReceiversState() receiverDelegate.grabReceiversState()
disposable += rxBus disposable += rxBus
.toObservable(EventNSClientStatus::class.java) .toObservable(EventNSClientStatus::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -109,14 +106,6 @@ class NSClientPlugin @Inject constructor(
// Pass to setup wizard // Pass to setup wizard
rxBus.send(EventSWSyncStatus(event.getStatus(context))) rxBus.send(EventSWSyncStatus(event.getStatus(context)))
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNetworkChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAppExit::class.java) .toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -128,10 +117,6 @@ class NSClientPlugin @Inject constructor(
addToLog(event) addToLog(event)
aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText) aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventChargingState::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNSClientResend::class.java) .toObservable(EventNSClientResend::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -181,7 +166,7 @@ class NSClientPlugin @Inject constructor(
} }
} }
override fun detectedNsVersion(): String? = nsSettingsStatus.getVersion() override fun detectedNsVersion(): String = nsSettingsStatus.getVersion()
private fun addToLog(ev: EventNSClientNewLog) { private fun addToLog(ev: EventNSClientNewLog) {
synchronized(listLog) { synchronized(listLog) {
@ -212,8 +197,8 @@ class NSClientPlugin @Inject constructor(
} }
override fun pause(newState: Boolean) { override fun pause(newState: Boolean) {
sp.putBoolean(R.string.key_ns_client_paused, newState) sp.putBoolean(R.string.key_ns_paused, newState)
rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_client_paused))) rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_paused)))
} }
override val address: String get() = nsClientService?.nsURL ?: "" override val address: String get() = nsClientService?.nsURL ?: ""

View file

@ -1,43 +1,67 @@
package info.nightscout.plugins.sync.nsclient package info.nightscout.plugins.sync.nsclient
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.receivers.ReceiverStatusStore import info.nightscout.interfaces.receivers.ReceiverStatusStore
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventChargingState import info.nightscout.rx.events.EventChargingState
import info.nightscout.rx.events.EventNetworkChange import info.nightscout.rx.events.EventNetworkChange
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@OpenForTesting @OpenForTesting
@Singleton @Singleton
class NsClientReceiverDelegate @Inject constructor( class ReceiverDelegate @Inject constructor(
private val rxBus: RxBus, private val rxBus: RxBus,
private val rh: ResourceHelper, private val rh: ResourceHelper,
private val sp: SP, private val sp: SP,
private val receiverStatusStore: ReceiverStatusStore private val receiverStatusStore: ReceiverStatusStore,
aapsSchedulers: AapsSchedulers,
fabricPrivacy: FabricPrivacy
) { ) {
private var allowedChargingState = true private var allowedChargingState: Boolean? = null
private var allowedNetworkState = true private var allowedNetworkState: Boolean? = null
var allowed = true var allowed: Boolean = false
var blockingReason = "" var blockingReason = "Status not available"
private val disposable = CompositeDisposable()
init {
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> onPreferenceChange(ev) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNetworkChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> onNetworkChange(ev) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventChargingState::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev -> onChargingStateChange(ev) }, fabricPrivacy::logException)
}
fun grabReceiversState() { fun grabReceiversState() {
receiverStatusStore.updateNetworkStatus() receiverStatusStore.updateNetworkStatus()
} }
fun onStatusEvent(ev: EventPreferenceChange) { private fun onPreferenceChange(ev: EventPreferenceChange) {
when { when {
ev.isChanged(rh.gs(R.string.key_ns_wifi)) || ev.isChanged(rh.gs(R.string.key_ns_wifi)) ||
ev.isChanged(rh.gs(R.string.key_ns_cellular)) || ev.isChanged(rh.gs(R.string.key_ns_cellular)) ||
ev.isChanged(rh.gs(R.string.key_ns_wifi_ssids)) || ev.isChanged(rh.gs(R.string.key_ns_wifi_ssids)) ||
ev.isChanged(rh.gs(R.string.key_ns_allow_roaming)) -> { ev.isChanged(rh.gs(R.string.key_ns_allow_roaming)) -> {
receiverStatusStore.updateNetworkStatus() receiverStatusStore.updateNetworkStatus()
receiverStatusStore.lastNetworkEvent?.let { onStatusEvent(it) } receiverStatusStore.lastNetworkEvent?.let { onNetworkChange(it) }
} }
ev.isChanged(rh.gs(R.string.key_ns_charging)) || ev.isChanged(rh.gs(R.string.key_ns_charging)) ||
@ -47,29 +71,30 @@ class NsClientReceiverDelegate @Inject constructor(
} }
} }
fun onStatusEvent(ev: EventChargingState) { private fun onChargingStateChange(ev: EventChargingState) {
val newChargingState = calculateStatus(ev) val newChargingState = calculateStatus(ev)
if (newChargingState != allowedChargingState) { if (newChargingState != allowedChargingState) {
allowedChargingState = newChargingState allowedChargingState = newChargingState
blockingReason = rh.gs(R.string.blocked_by_charging) if (!newChargingState) blockingReason = rh.gs(R.string.blocked_by_charging)
processStateChange() processStateChange()
} }
} }
fun onStatusEvent(ev: EventNetworkChange) { private fun onNetworkChange(ev: EventNetworkChange) {
val newNetworkState = calculateStatus(ev) val newNetworkState = calculateStatus(ev)
if (newNetworkState != allowedNetworkState) { if (newNetworkState != allowedNetworkState) {
allowedNetworkState = newNetworkState allowedNetworkState = newNetworkState
blockingReason = rh.gs(R.string.blocked_by_connectivity) if (!newNetworkState) blockingReason = rh.gs(R.string.blocked_by_connectivity)
processStateChange() processStateChange()
} }
} }
private fun processStateChange() { private fun processStateChange() {
val newAllowedState = allowedChargingState && allowedNetworkState val newAllowedState = allowedChargingState == true && allowedNetworkState == true
if (newAllowedState != allowed) { if (newAllowedState != allowed) {
allowed = newAllowedState allowed = newAllowedState
rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_client_paused))) if (allowed) blockingReason = ""
rxBus.send(EventConnectivityOptionChanged(blockingReason))
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.plugins.sync.nsclient.data package info.nightscout.plugins.sync.nsclient.data
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.configBuilder.RunningConfiguration import info.nightscout.interfaces.configBuilder.RunningConfiguration
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
@ -65,6 +66,7 @@ import javax.inject.Singleton
*/ */
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
@Singleton @Singleton
@OpenForTesting
class NSDeviceStatusHandler @Inject constructor( class NSDeviceStatusHandler @Inject constructor(
private val sp: SP, private val sp: SP,
private val config: Config, private val config: Config,

View file

@ -31,6 +31,7 @@ import info.nightscout.interfaces.utils.JsonHelper.safeGetStringAllowNull
import info.nightscout.interfaces.workflow.WorkerClasses import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged
import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsclient.NSClientPlugin import info.nightscout.plugins.sync.nsclient.NSClientPlugin
@ -49,6 +50,7 @@ import info.nightscout.rx.events.EventConfigBuilderChange
import info.nightscout.rx.events.EventDismissNotification import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNSClientRestart import info.nightscout.rx.events.EventNSClientRestart
import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
@ -139,13 +141,21 @@ class NSClientService : DaggerService() {
.subscribe({ event: EventPreferenceChange -> .subscribe({ event: EventPreferenceChange ->
if (event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)) || if (event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)) ||
event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_api_secret)) || event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_api_secret)) ||
event.isChanged(rh.gs(R.string.key_ns_client_paused)) event.isChanged(rh.gs(R.string.key_ns_paused))
) { ) {
latestDateInReceivedData = 0 latestDateInReceivedData = 0
destroy() destroy()
initialize() initialize()
} }
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventConnectivityOptionChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
latestDateInReceivedData = 0
destroy()
initialize()
}, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAppExit::class.java) .toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -165,6 +175,10 @@ class NSClientService : DaggerService() {
.toObservable(NSAuthAck::class.java) .toObservable(NSAuthAck::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ ack -> processAuthAck(ack) }, fabricPrivacy::logException) .subscribe({ ack -> processAuthAck(ack) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ resend("NEW_DATA") }, fabricPrivacy::logException)
} }
override fun onDestroy() { override fun onDestroy() {
@ -213,10 +227,10 @@ class NSClientService : DaggerService() {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString() if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString()
rxBus.send(EventNSClientStatus("Initializing")) rxBus.send(EventNSClientStatus("Initializing"))
if (!nsClientPlugin.isAllowed) { if (nsClientPlugin.isAllowed != true) {
rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason)) rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason))
rxBus.send(EventNSClientStatus(nsClientPlugin.blockingReason)) rxBus.send(EventNSClientStatus(nsClientPlugin.blockingReason))
} else if (sp.getBoolean(R.string.key_ns_client_paused, false)) { } else if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused")) rxBus.send(EventNSClientNewLog("NSCLIENT", "paused"))
rxBus.send(EventNSClientStatus("Paused")) rxBus.send(EventNSClientStatus("Paused"))
} else if (!nsEnabled) { } else if (!nsEnabled) {
@ -225,16 +239,10 @@ class NSClientService : DaggerService() {
} else if (nsURL != "" && (nsURL.lowercase(Locale.getDefault()).startsWith("https://"))) { } else if (nsURL != "" && (nsURL.lowercase(Locale.getDefault()).startsWith("https://"))) {
try { try {
rxBus.send(EventNSClientStatus("Connecting ...")) rxBus.send(EventNSClientStatus("Connecting ..."))
val opt = IO.Options() val opt = IO.Options().also { it.forceNew = true }
opt.forceNew = true
opt.reconnection = true
socket = IO.socket(nsURL, opt).also { socket -> socket = IO.socket(nsURL, opt).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnect) socket.on(Socket.EVENT_CONNECT, onConnect)
socket.on(Socket.EVENT_DISCONNECT, onDisconnect) socket.on(Socket.EVENT_DISCONNECT, onDisconnect)
socket.on(Socket.EVENT_ERROR, onError)
socket.on(Socket.EVENT_CONNECT_ERROR, onError)
socket.on(Socket.EVENT_CONNECT_TIMEOUT, onError)
socket.on(Socket.EVENT_PING, onPing)
rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect")) rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect"))
socket.connect() socket.connect()
socket.on("dataUpdate", onDataUpdate) socket.on("dataUpdate", onDataUpdate)
@ -300,7 +308,6 @@ class NSClientService : DaggerService() {
@Synchronized fun destroy() { @Synchronized fun destroy() {
socket?.off(Socket.EVENT_CONNECT) socket?.off(Socket.EVENT_CONNECT)
socket?.off(Socket.EVENT_DISCONNECT) socket?.off(Socket.EVENT_DISCONNECT)
socket?.off(Socket.EVENT_PING)
socket?.off("dataUpdate") socket?.off("dataUpdate")
socket?.off("announcement") socket?.off("announcement")
socket?.off("alarm") socket?.off("alarm")
@ -336,18 +343,6 @@ class NSClientService : DaggerService() {
nsDevice = sp.getString("careportal_enteredby", "") nsDevice = sp.getString("careportal_enteredby", "")
} }
private val onError = Emitter.Listener { args ->
var msg = "Unknown Error"
if (args.isNotEmpty() && args[0] != null) {
msg = args[0].toString()
}
rxBus.send(EventNSClientNewLog("ERROR", msg))
}
private val onPing = Emitter.Listener {
rxBus.send(EventNSClientNewLog("PING", "received"))
// send data if there is something waiting
resend("Ping received")
}
private val onAnnouncement = Emitter.Listener { args -> private val onAnnouncement = Emitter.Listener { args ->
/* /*
@ -651,7 +646,7 @@ class NSClientService : DaggerService() {
private fun handleAlarm(alarm: JSONObject) { private fun handleAlarm(alarm: JSONObject) {
val defaultVal = config.NSCLIENT val defaultVal = config.NSCLIENT
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_alarms, defaultVal)) { if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_alarms, defaultVal)) {
val snoozedTo = sp.getLong(info.nightscout.core.utils.R.string.key_snoozed_to, 0L) val snoozedTo = sp.getLong(rh.gs(info.nightscout.core.utils.R.string.key_snoozed_to) + alarm.optString("level"), 0L)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) { if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
val nsAlarm = NSAlarm(alarm) val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm) uiInteraction.addNotificationWithAction(injector, nsAlarm)
@ -664,7 +659,7 @@ class NSClientService : DaggerService() {
private fun handleUrgentAlarm(alarm: JSONObject) { private fun handleUrgentAlarm(alarm: JSONObject) {
val defaultVal = config.NSCLIENT val defaultVal = config.NSCLIENT
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_alarms, defaultVal)) { if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_alarms, defaultVal)) {
val snoozedTo = sp.getLong(info.nightscout.core.utils.R.string.key_snoozed_to, 0L) val snoozedTo = sp.getLong(rh.gs(info.nightscout.core.utils.R.string.key_snoozed_to) + alarm.optString("level"), 0L)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) { if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
val nsAlarm = NSAlarm(alarm) val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm) uiInteraction.addNotificationWithAction(injector, nsAlarm)

View file

@ -3,6 +3,7 @@ package info.nightscout.plugins.sync.nsclientV3
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.os.SystemClock
import android.text.Spanned import android.text.Spanned
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
@ -16,26 +17,33 @@ import com.google.gson.GsonBuilder
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.database.ValueWrapper import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.interfaces.TraceableDBEntry import info.nightscout.database.entities.interfaces.TraceableDBEntry
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HtmlHelper import info.nightscout.interfaces.utils.HtmlHelper
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.NSClientFragment import info.nightscout.plugins.sync.nsShared.NSClientFragment
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged
import info.nightscout.plugins.sync.nsShared.events.EventNSClientResend import info.nightscout.plugins.sync.nsShared.events.EventNSClientResend
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsclient.NsClientReceiverDelegate import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolus import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolus
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolusWizard import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolusWizard
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSCarbs import info.nightscout.plugins.sync.nsclientV3.extensions.toNSCarbs
@ -49,16 +57,17 @@ import info.nightscout.plugins.sync.nsclientV3.extensions.toNSSvgV3
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryBasal import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryBasal
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryTarget import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryTarget
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTherapyEvent import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTherapyEvent
import info.nightscout.plugins.sync.nsclientV3.workers.DataSyncWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessFoodWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessTreatmentsWorker
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventChargingState import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNetworkChange
import info.nightscout.rx.events.EventNewBG
import info.nightscout.rx.events.EventNewHistoryData import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventSWSyncStatus import info.nightscout.rx.events.EventSWSyncStatus
@ -66,6 +75,10 @@ import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.NSAndroidClientImpl import info.nightscout.sdk.NSAndroidClientImpl
import info.nightscout.sdk.interfaces.NSAndroidClient import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.mapper.toNSDeviceStatus
import info.nightscout.sdk.mapper.toNSFood
import info.nightscout.sdk.mapper.toNSSgvV3
import info.nightscout.sdk.mapper.toNSTreatment
import info.nightscout.sdk.remotemodel.LastModified import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
@ -73,15 +86,19 @@ import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import io.socket.client.Ack
import io.socket.client.IO
import io.socket.client.Socket
import io.socket.emitter.Emitter
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.util.concurrent.Executors import org.json.JSONArray
import java.util.concurrent.ScheduledFuture import org.json.JSONObject
import java.util.concurrent.TimeUnit import java.net.URISyntaxException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -96,13 +113,18 @@ class NSClientV3Plugin @Inject constructor(
private val context: Context, private val context: Context,
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val sp: SP, private val sp: SP,
private val nsClientReceiverDelegate: NsClientReceiverDelegate, private val receiverDelegate: ReceiverDelegate,
private val config: Config, private val config: Config,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val uiInteraction: UiInteraction, private val uiInteraction: UiInteraction,
private val dataSyncSelector: DataSyncSelector, private val dataSyncSelector: DataSyncSelector,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val repository: AppRepository private val repository: AppRepository,
private val nsDeviceStatusHandler: NSDeviceStatusHandler,
private val workManager: WorkManager,
private val workerClasses: WorkerClasses,
private val dataWorkerStorage: DataWorkerStorage,
private val nsClientSource: NSClientSource
) : NsClient, Sync, PluginBase( ) : NsClient, Sync, PluginBase(
PluginDescription() PluginDescription()
.mainType(PluginType.SYNC) .mainType(PluginType.SYNC)
@ -115,13 +137,14 @@ class NSClientV3Plugin @Inject constructor(
aapsLogger, rh, injector aapsLogger, rh, injector
) { ) {
@Suppress("PropertyName")
val JOB_NAME: String = this::class.java.simpleName
companion object { companion object {
val REFRESH_INTERVAL = T.secs(30).msecs()
const val RECORDS_TO_LOAD = 500 const val RECORDS_TO_LOAD = 500
} }
@Suppress("PropertyName") val JOB_NAME: String = this::class.java.simpleName
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
var scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) var scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private lateinit var runLoop: Runnable private lateinit var runLoop: Runnable
@ -130,8 +153,10 @@ class NSClientV3Plugin @Inject constructor(
override val status override val status
get() = get() =
when { when {
sp.getBoolean(R.string.key_ns_client_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused) sp.getBoolean(R.string.key_ns_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused)
isAllowed.not() -> blockingReason isAllowed.not() -> blockingReason
sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && wsConnected -> "WS: " + rh.gs(info.nightscout.shared.R.string.connected)
sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && !wsConnected -> "WS: " + rh.gs(R.string.not_connected)
lastOperationError != null -> rh.gs(info.nightscout.core.ui.R.string.error) lastOperationError != null -> rh.gs(info.nightscout.core.ui.R.string.error)
nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected) nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected)
workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working) workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working)
@ -143,8 +168,8 @@ class NSClientV3Plugin @Inject constructor(
internal var nsAndroidClient: NSAndroidClient? = null internal var nsAndroidClient: NSAndroidClient? = null
private val isAllowed get() = nsClientReceiverDelegate.allowed private val isAllowed get() = receiverDelegate.allowed
private val blockingReason get() = nsClientReceiverDelegate.blockingReason private val blockingReason get() = receiverDelegate.blockingReason
val maxAge = T.days(77).msecs() val maxAge = T.days(77).msecs()
internal var newestDataOnServer: LastModified? = null // timestamp of last modification for every collection provided by server internal var newestDataOnServer: LastModified? = null // timestamp of last modification for every collection provided by server
@ -161,24 +186,32 @@ class NSClientV3Plugin @Inject constructor(
) )
) )
setClient() setClient("START")
nsClientReceiverDelegate.grabReceiversState() receiverDelegate.grabReceiversState()
disposable += rxBus disposable += rxBus
.toObservable(EventNetworkChange::class.java) .toObservable(EventConnectivityOptionChanged::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ ev -> .subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev) rxBus.send(EventNSClientNewLog("● CONNECTIVITY", ev.blockingReason))
setClient() setClient("CONNECTIVITY")
rxBus.send(EventNSClientUpdateGUI()) if (isAllowed) executeLoop("CONNECTIVITY", forceNew = false)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ ev -> .subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev) if (ev.isChanged(rh.gs(R.string.key_ns_client_token)) ||
if (ev.isChanged(rh.gs(R.string.key_ns_client_token)) || ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url))) ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)) ||
setClient() ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_ns_use_ws)) ||
ev.isChanged(rh.gs(R.string.key_ns_paused)) ||
ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_ns_alarms)) ||
ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_ns_announcements))
)
setClient("SETTING CHANGE")
if (ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_local_profile_last_change)))
executeUpload("PROFILE_CHANGE", forceNew = true)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAppExit::class.java) .toObservable(EventAppExit::class.java)
@ -191,46 +224,56 @@ class NSClientV3Plugin @Inject constructor(
addToLog(event) addToLog(event)
aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText) aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventChargingState::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev)
rxBus.send(EventNSClientUpdateGUI())
}, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNSClientResend::class.java) .toObservable(EventNSClientResend::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException) .subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ delayAndScheduleExecution("NEW_BG") }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNewHistoryData::class.java) .toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ delayAndScheduleExecution("NEW_DATA") }, fabricPrivacy::logException) .subscribe({ executeUpload("NEW_DATA", forceNew = false) }, fabricPrivacy::logException)
runLoop = Runnable { runLoop = Runnable {
handler.postDelayed(runLoop, REFRESH_INTERVAL) var refreshInterval = T.mins(5).msecs()
repository.getLastGlucoseValueWrapped().blockingGet().let { repository.getLastGlucoseValueWrapped().blockingGet().let {
// if last value is older than 5 min or there is no bg // if last value is older than 5 min or there is no bg
if (it is ValueWrapper.Existing) { if (it is ValueWrapper.Existing) {
if (it.value.timestamp < dateUtil.now() - T.mins(5).plus(T.secs(20)).msecs()) if (it.value.timestamp < dateUtil.now() - T.mins(5).plus(T.secs(20)).msecs()) {
refreshInterval = T.mins(1).msecs()
executeLoop("MAIN_LOOP", forceNew = false) executeLoop("MAIN_LOOP", forceNew = false)
else {
if (isAllowed) rxBus.send(EventNSClientNewLog("RECENT", "No need to load"))
} }
} else executeLoop("MAIN_LOOP", forceNew = false) } else executeLoop("MAIN_LOOP", forceNew = false)
} }
handler.postDelayed(runLoop, refreshInterval)
rxBus.send(EventNSClientNewLog("● TICK", ""))
}
handler.postDelayed(runLoop, T.mins(2).msecs())
}
fun scheduleIrregularExecution(refreshToken: Boolean = false) {
if (refreshToken) {
handler.post { executeLoop("REFRESH TOKEN", forceNew = true) }
return
}
if (config.NSCLIENT || nsClientSource.isEnabled()) {
var origin = "5_MIN_AFTER_BG"
var forceNew = true
var toTime = lastLoadedSrvModified.collections.entries + T.mins(5).plus(T.secs(10)).msecs()
if (toTime < dateUtil.now()) {
toTime = dateUtil.now() + T.mins(1).plus(T.secs(0)).msecs()
origin = "1_MIN_OLD_DATA"
forceNew = false
}
handler.postDelayed({ executeLoop(origin, forceNew = forceNew) }, toTime - dateUtil.now())
rxBus.send(EventNSClientNewLog("● NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
} }
handler.postDelayed(runLoop, REFRESH_INTERVAL)
executeLoop("START", forceNew = false)
} }
override fun onStop() { override fun onStop() {
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
disposable.clear() disposable.clear()
storageSocket?.disconnect()
alarmSocket?.disconnect()
super.onStop() super.onStop()
} }
@ -254,7 +297,7 @@ class NSClientV3Plugin @Inject constructor(
} }
} }
private fun setClient() { private fun setClient(reason: String) {
nsAndroidClient = NSAndroidClientImpl( nsAndroidClient = NSAndroidClientImpl(
baseUrl = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").lowercase().replace("https://", "").replace(Regex("/$"), ""), baseUrl = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").lowercase().replace("https://", "").replace(Regex("/$"), ""),
accessToken = sp.getString(R.string.key_ns_client_token, ""), accessToken = sp.getString(R.string.key_ns_client_token, ""),
@ -262,9 +305,266 @@ class NSClientV3Plugin @Inject constructor(
logging = true, logging = true,
logger = { msg -> aapsLogger.debug(LTag.HTTP, msg) } logger = { msg -> aapsLogger.debug(LTag.HTTP, msg) }
) )
if (wsConnected) {
storageSocket?.disconnect()
alarmSocket?.disconnect()
}
SystemClock.sleep(2000)
initializeWebSockets(reason)
rxBus.send(EventSWSyncStatus(status)) rxBus.send(EventSWSyncStatus(status))
} }
/**********************
WS code
**********************/
private var storageSocket: Socket? = null
private var alarmSocket: Socket? = null
var wsConnected = false
internal var initialLoadFinished = false
private fun initializeWebSockets(reason: String) {
if (!sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true)) return
if (sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").isEmpty()) return
val urlStorage = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").lowercase().replace(Regex("/$"), "") + "/storage"
val urlAlarm = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").lowercase().replace(Regex("/$"), "") + "/alarm"
if (!isAllowed) {
rxBus.send(EventNSClientNewLog("● WS", blockingReason))
} else if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("● WS", "paused"))
} else {
try {
// java io.client doesn't support multiplexing. create 2 sockets
storageSocket = IO.socket(urlStorage).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnectStorage)
socket.on(Socket.EVENT_DISCONNECT, onDisconnectStorage)
rxBus.send(EventNSClientNewLog("► WS", "do connect storage $reason"))
socket.connect()
socket.on("create", onDataCreateUpdate)
socket.on("update", onDataCreateUpdate)
socket.on("delete", onDataDelete)
}
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_announcements, config.NSCLIENT) ||
sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_alarms, config.NSCLIENT)
)
alarmSocket = IO.socket(urlAlarm).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnectAlarms)
socket.on(Socket.EVENT_DISCONNECT, onDisconnectAlarm)
rxBus.send(EventNSClientNewLog("► WS", "do connect alarm $reason"))
socket.connect()
socket.on("announcement", onAnnouncement)
socket.on("alarm", onAlarm)
socket.on("urgent_alarm", onUrgentAlarm)
socket.on("clear_alarm", onClearAlarm)
}
} catch (e: URISyntaxException) {
rxBus.send(EventNSClientNewLog("● WS", "Wrong URL syntax"))
} catch (e: RuntimeException) {
rxBus.send(EventNSClientNewLog("● WS", "RuntimeException"))
}
}
}
private val onConnectStorage = Emitter.Listener {
val socketId = storageSocket?.id() ?: "NULL"
rxBus.send(EventNSClientNewLog("◄ WS", "connected storage ID: $socketId"))
if (storageSocket != null) {
val authMessage = JSONObject().also {
it.put("accessToken", sp.getString(R.string.key_ns_client_token, ""))
it.put("collections", JSONArray(arrayOf("devicestatus", "entries", "profile", "treatments", "foods", "settings")))
}
rxBus.send(EventNSClientNewLog("► WS", "requesting auth for storage"))
storageSocket?.emit("subscribe", authMessage, Ack { args ->
val response = args[0] as JSONObject
wsConnected = if (response.optBoolean("success")) {
rxBus.send(EventNSClientNewLog("◄ WS", "Subscribed for: ${response.optString("collections")}"))
true
} else {
rxBus.send(EventNSClientNewLog("◄ WS", "Auth failed"))
false
}
})
}
}
private val onConnectAlarms = Emitter.Listener {
val socketId = alarmSocket?.id() ?: "NULL"
rxBus.send(EventNSClientNewLog("◄ WS", "connected alarms ID: $socketId"))
if (alarmSocket != null) {
val authMessage = JSONObject().also {
it.put("accessToken", sp.getString(R.string.key_ns_client_token, ""))
}
rxBus.send(EventNSClientNewLog("► WS", "requesting auth for alarms"))
alarmSocket?.emit("subscribe", authMessage, Ack { args ->
val response = args[0] as JSONObject
wsConnected = if (response.optBoolean("success")) {
rxBus.send(EventNSClientNewLog("◄ WS", response.optString("message")))
true
} else {
rxBus.send(EventNSClientNewLog("◄ WS", "Auth failed"))
false
}
})
}
}
private val onDisconnectStorage = Emitter.Listener { args ->
aapsLogger.debug(LTag.NSCLIENT, "disconnect storage reason: ${args[0]}")
rxBus.send(EventNSClientNewLog("◄ WS", "disconnect storage event"))
wsConnected = false
initialLoadFinished = false
storageSocket = null
}
private val onDisconnectAlarm = Emitter.Listener { args ->
aapsLogger.debug(LTag.NSCLIENT, "disconnect alarm reason: ${args[0]}")
rxBus.send(EventNSClientNewLog("◄ WS", "disconnect alarm event"))
alarmSocket = null
}
private val onDataCreateUpdate = Emitter.Listener { args ->
val response = args[0] as JSONObject
aapsLogger.debug(LTag.NSCLIENT, "onDataCreateUpdate: $response")
val collection = response.getString("colName")
val docJson = response.getJSONObject("doc")
val docString = response.getString("doc")
rxBus.send(EventNSClientNewLog("◄ WS CREATE/UPDATE", "$collection <i>$docString</i>"))
val srvModified = docJson.getLong("srvModified")
lastLoadedSrvModified.set(collection, srvModified)
storeLastLoadedSrvModified()
when (collection) {
"devicestatus" -> docString.toNSDeviceStatus().let { nsDeviceStatusHandler.handleNewData(arrayOf(it)) }
"entries" -> docString.toNSSgvV3()?.let {
workManager.beginUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(workerClasses.nsClientSourceWorker).setInputData(dataWorkerStorage.storeInputData(listOf(it))).build()
)
.then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreBgWorker::class.java).build())
.enqueue()
}
"profile" ->
workManager.enqueueUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(workerClasses.nsProfileWorker).setInputData(dataWorkerStorage.storeInputData(docJson)).build()
)
"treatments" -> docString.toNSTreatment()?.let {
workManager.beginUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java).setInputData(dataWorkerStorage.storeInputData(listOf(it))).build()
)
.then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreTreatmentsWorker::class.java).build())
.enqueue()
}
"foods" -> docString.toNSFood()?.let {
workManager.beginUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessFoodWorker::class.java).setInputData(dataWorkerStorage.storeInputData(listOf(it))).build()
)
.then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreFoodWorker::class.java).build())
.enqueue()
}
"settings" -> {}
}
}
private val onDataDelete = Emitter.Listener { args ->
val response = args[0] as JSONObject
aapsLogger.debug(LTag.NSCLIENT, "onDataDelete: $response")
rxBus.send(EventNSClientNewLog("◄ WS DELETE", "${response.optString("collection")} ${response.optString("doc")}"))
}
private val onAnnouncement = Emitter.Listener { args ->
/*
{
"level":0,
"title":"Announcement",
"message":"test",
"plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true},
"group":"Announcement",
"isAnnouncement":true,
"key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da"
}
*/
val data = args[0] as JSONObject
rxBus.send(EventNSClientNewLog("◄ ANNOUNCEMENT", data.optString("message")))
aapsLogger.debug(LTag.NSCLIENT, data.toString())
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_announcements, config.NSCLIENT))
uiInteraction.addNotificationWithAction(injector, NSAlarm(data))
}
private val onAlarm = Emitter.Listener { args ->
/*
{
"level":1,
"title":"Warning HIGH",
"message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g",
"eventName":"high",
"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true},
"pushoverSound":"climb",
"debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}},
"group":"default",
"key":"simplealarms_1"
}
*/
val data = args[0] as JSONObject
rxBus.send(EventNSClientNewLog("◄ ALARM", data.optString("message")))
aapsLogger.debug(LTag.NSCLIENT, data.toString())
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_alarms, config.NSCLIENT)) {
val snoozedTo = sp.getLong(rh.gs(info.nightscout.core.utils.R.string.key_snoozed_to) + data.optString("level"), 0L)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo)
uiInteraction.addNotificationWithAction(injector, NSAlarm(data))
}
}
private val onUrgentAlarm = Emitter.Listener { args: Array<Any> ->
val data = args[0] as JSONObject
rxBus.send(EventNSClientNewLog("◄ URGENT ALARM", data.optString("message")))
aapsLogger.debug(LTag.NSCLIENT, data.toString())
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_alarms, config.NSCLIENT)) {
val snoozedTo = sp.getLong(rh.gs(info.nightscout.core.utils.R.string.key_snoozed_to) + data.optString("level"), 0L)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo)
uiInteraction.addNotificationWithAction(injector, NSAlarm(data))
}
}
private val onClearAlarm = Emitter.Listener { args ->
/*
{
"clear":true,
"title":"All Clear",
"message":"default - Urgent was ack'd",
"group":"default"
}
*/
val data = args[0] as JSONObject
rxBus.send(EventNSClientNewLog("◄ CLEARALARM", data.optString("title")))
aapsLogger.debug(LTag.NSCLIENT, data.toString())
rxBus.send(EventDismissNotification(Notification.NS_ALARM))
rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
}
override fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long) {
if (!isEnabled()) return
if (!sp.getBoolean(R.string.key_ns_upload, true)) {
aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped")
return
}
alarmSocket?.emit("ack", originalAlarm.level(), originalAlarm.group(), silenceTimeInMilliseconds)
rxBus.send(EventNSClientNewLog("► ALARMACK ", "${originalAlarm.level()} ${originalAlarm.group()} $silenceTimeInMilliseconds"))
}
/**********************
WS code end
**********************/
private fun addToLog(ev: EventNSClientNewLog) { private fun addToLog(ev: EventNSClientNewLog) {
synchronized(listLog) { synchronized(listLog) {
listLog.add(ev) listLog.add(ev)
@ -290,32 +590,21 @@ class NSClientV3Plugin @Inject constructor(
} }
override fun resend(reason: String) { override fun resend(reason: String) {
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true))
executeUpload("RESEND", forceNew = false)
else
executeLoop("RESEND", forceNew = false) executeLoop("RESEND", forceNew = false)
} }
override fun pause(newState: Boolean) { override fun pause(newState: Boolean) {
sp.putBoolean(R.string.key_ns_client_paused, newState) sp.putBoolean(R.string.key_ns_paused, newState)
rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_client_paused))) rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_paused)))
} }
override fun detectedNsVersion(): String? = nsAndroidClient?.lastStatus?.version override fun detectedNsVersion(): String? = nsAndroidClient?.lastStatus?.version
override val address: String get() = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "") override val address: String get() = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "")
override fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long) {
if (!isEnabled()) return
if (!sp.getBoolean(R.string.key_ns_upload, true)) {
aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped")
return
}
// nsClientService?.sendAlarmAck(
// AlarmAck().also { ack ->
// ack.level = originalAlarm.level()
// ack.group = originalAlarm.group()
// ack.silenceTime = silenceTimeInMilliseconds
// })
}
override fun isFirstLoad(collection: NsClient.Collection) = override fun isFirstLoad(collection: NsClient.Collection) =
when (collection) { when (collection) {
NsClient.Collection.ENTRIES -> lastLoadedSrvModified.collections.entries == 0L NsClient.Collection.ENTRIES -> lastLoadedSrvModified.collections.entries == 0L
@ -335,6 +624,7 @@ class NSClientV3Plugin @Inject constructor(
override fun resetToFullSync() { override fun resetToFullSync() {
firstLoadContinueTimestamp = LastModified(LastModified.Collections()) firstLoadContinueTimestamp = LastModified(LastModified.Collections())
lastLoadedSrvModified = LastModified(LastModified.Collections()) lastLoadedSrvModified = LastModified(LastModified.Collections())
initialLoadFinished = false
storeLastLoadedSrvModified() storeLastLoadedSrvModified()
dataSyncSelector.resetToNextFullSync() dataSyncSelector.resetToNextFullSync()
} }
@ -354,15 +644,15 @@ class NSClientV3Plugin @Inject constructor(
val data = (dataPair as DataSyncSelector.PairProfileStore).value val data = (dataPair as DataSyncSelector.PairProfileStore).value
scope.launch { scope.launch {
try { try {
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} $data $progress")) rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} <i>$data</i> $progress"))
nsAndroidClient?.createProfileStore(data)?.let { result -> nsAndroidClient?.createProfileStore(data)?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ProfileStore")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ProfileStore"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ProfileStore")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ProfileStore"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "ProfileStore")) rxBus.send(EventNSClientNewLog("ERROR", "ProfileStore"))
return@launch return@launch
} }
} }
@ -384,15 +674,15 @@ class NSClientV3Plugin @Inject constructor(
val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toNSDeviceStatus() val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toNSDeviceStatus()
scope.launch { scope.launch {
try { try {
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress")) rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"))
nsAndroidClient?.createDeviceStatus(data)?.let { result -> nsAndroidClient?.createDeviceStatus(data)?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName} ${result.identifier}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName} ${result.identifier}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -425,24 +715,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send( rxBus.send(
EventNSClientNewLog( EventNSClientNewLog(
when (operation) { when (operation) {
Operation.CREATE -> "ADD $collection" Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection" Operation.UPDATE -> "UPDATE $collection"
}, },
when (operation) { when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress" Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress" Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
} }
) )
) )
call?.let { it(data) }?.let { result -> call?.let { it(data) }?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -480,24 +770,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send( rxBus.send(
EventNSClientNewLog( EventNSClientNewLog(
when (operation) { when (operation) {
Operation.CREATE -> "ADD $collection" Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection" Operation.UPDATE -> "UPDATE $collection"
}, },
when (operation) { when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress" Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress" Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
} }
) )
) )
call?.let { it(data) }?.let { result -> call?.let { it(data) }?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -563,24 +853,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send( rxBus.send(
EventNSClientNewLog( EventNSClientNewLog(
when (operation) { when (operation) {
Operation.CREATE -> "ADD $collection" Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection" Operation.UPDATE -> "UPDATE $collection"
}, },
when (operation) { when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress" Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress" Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
} }
) )
) )
call?.let { it(data) }?.let { result -> call?.let { it(data) }?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -707,35 +997,23 @@ class NSClientV3Plugin @Inject constructor(
sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified)) sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified))
} }
fun scheduleIrregularExecution() {
var origin = "5_MIN_AFTER_BG"
var forceNew = true
var toTime = lastLoadedSrvModified.collections.entries + T.mins(5).plus(T.secs(10)).msecs()
if (toTime < dateUtil.now()) {
toTime = dateUtil.now() + T.mins(1).plus(T.secs(0)).msecs()
origin = "1_MIN_OLD_DATA"
forceNew = false
}
handler.postDelayed({ executeLoop(origin, forceNew = forceNew) }, toTime - dateUtil.now())
rxBus.send(EventNSClientNewLog("NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
}
private fun executeLoop(origin: String, forceNew: Boolean) { private fun executeLoop(origin: String, forceNew: Boolean) {
if (sp.getBoolean(R.string.key_ns_client_paused, false)) { if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && initialLoadFinished) return
rxBus.send(EventNSClientNewLog("RUN", "paused")) if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("● RUN", "paused $origin"))
return return
} }
if (!isAllowed) { if (!isAllowed) {
rxBus.send(EventNSClientNewLog("RUN", blockingReason)) rxBus.send(EventNSClientNewLog("● RUN", "$blockingReason $origin"))
return return
} }
if (workIsRunning(arrayOf(JOB_NAME))) { if (workIsRunning(arrayOf(JOB_NAME))) {
rxBus.send(EventNSClientNewLog("RUN", "Already running $origin")) rxBus.send(EventNSClientNewLog("● RUN", "Already running $origin"))
if (!forceNew) return if (!forceNew) return
// Wait for end and start new cycle // Wait for end and start new cycle
while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000) while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000)
} }
rxBus.send(EventNSClientNewLog("RUN", "Starting next round $origin")) rxBus.send(EventNSClientNewLog("RUN", "Starting next round $origin"))
WorkManager.getInstance(context) WorkManager.getInstance(context)
.beginUniqueWork( .beginUniqueWork(
JOB_NAME, JOB_NAME,
@ -753,6 +1031,30 @@ class NSClientV3Plugin @Inject constructor(
.enqueue() .enqueue()
} }
private fun executeUpload(origin: String, forceNew: Boolean) {
if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("● RUN", "paused"))
return
}
if (!isAllowed) {
rxBus.send(EventNSClientNewLog("● RUN", blockingReason))
return
}
if (workIsRunning(arrayOf(JOB_NAME))) {
rxBus.send(EventNSClientNewLog("● RUN", "Already running $origin"))
if (!forceNew) return
// Wait for end and start new cycle
while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000)
}
rxBus.send(EventNSClientNewLog("● RUN", "Starting upload $origin"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
JOB_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.Builder(DataSyncWorker::class.java).build()
)
}
private fun workIsRunning(workNames: Array<String>): Boolean { private fun workIsRunning(workNames: Array<String>): Boolean {
for (workName in workNames) for (workName in workNames)
for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get()) for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get())
@ -760,20 +1062,4 @@ class NSClientV3Plugin @Inject constructor(
return true return true
return false return false
} }
private val eventWorker = Executors.newSingleThreadScheduledExecutor()
private var scheduledEventPost: ScheduledFuture<*>? = null
private fun delayAndScheduleExecution(origin: String) {
class PostRunnable : Runnable {
override fun run() {
scheduledEventPost = null
executeLoop(origin, forceNew = true)
}
}
// cancel waiting task to prevent sending multiple posts
scheduledEventPost?.cancel(false)
val task: Runnable = PostRunnable()
scheduledEventPost = eventWorker.schedule(task, 10, TimeUnit.SECONDS)
}
} }

View file

@ -7,6 +7,7 @@ import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -20,12 +21,17 @@ class DataSyncWorker(
@Inject lateinit var dataSyncSelector: DataSyncSelector @Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
override suspend fun doWorkAndLog(): Result { override suspend fun doWorkAndLog(): Result {
if (activePlugin.activeNsClient?.hasWritePermission == true) { if (activePlugin.activeNsClient?.hasWritePermission == true || nsClientV3Plugin.wsConnected) {
rxBus.send(EventNSClientNewLog("UPL", "Start")) rxBus.send(EventNSClientNewLog("UPL", "Start"))
dataSyncSelector.doUpload() dataSyncSelector.doUpload()
rxBus.send(EventNSClientNewLog("UPL", "End")) rxBus.send(EventNSClientNewLog("► UPL", "End"))
} else {
rxBus.send(EventNSClientNewLog("► ERROR", "Not connected or write permission"))
// refresh token
nsClientV3Plugin.scheduleIrregularExecution(refreshToken = true)
} }
rxBus.send(EventNSClientUpdateGUI()) rxBus.send(EventNSClientUpdateGUI())
return Result.success() return Result.success()

View file

@ -71,7 +71,7 @@ class LoadBgWorker(
aapsLogger.debug(LTag.NSCLIENT, "SGVS: $sgvs") aapsLogger.debug(LTag.NSCLIENT, "SGVS: $sgvs")
if (sgvs.isNotEmpty()) { if (sgvs.isNotEmpty()) {
val action = if (isFirstLoad) "RCV-FIRST" else "RCV" val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
rxBus.send(EventNSClientNewLog(action, "${sgvs.size} SVGs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("$action", "${sgvs.size} SVGs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
// Objective0 // Objective0
sp.putBoolean(info.nightscout.core.utils.R.string.key_objectives_bg_is_available_in_ns, true) sp.putBoolean(info.nightscout.core.utils.R.string.key_objectives_bg_is_available_in_ns, true)
// Schedule processing of fetched data and continue of loading // Schedule processing of fetched data and continue of loading
@ -92,7 +92,7 @@ class LoadBgWorker(
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
nsClientV3Plugin.storeLastLoadedSrvModified() nsClientV3Plugin.storeLastLoadedSrvModified()
} }
rxBus.send(EventNSClientNewLog("RCV END", "No SGVs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("◄ RCV BG END", "No data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
workManager workManager
.beginUniqueWork( .beginUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
@ -108,7 +108,7 @@ class LoadBgWorker(
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
nsClientV3Plugin.storeLastLoadedSrvModified() nsClientV3Plugin.storeLastLoadedSrvModified()
} }
rxBus.send(EventNSClientNewLog("RCV END", "No new SGVs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("RCV BG END", "No new data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
workManager workManager
.beginUniqueWork( .beginUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
@ -120,7 +120,7 @@ class LoadBgWorker(
} }
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage nsClientV3Plugin.lastOperationError = error.localizedMessage
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -33,15 +33,18 @@ class LoadDeviceStatusWorker(
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null")) val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
try { try {
// Notify plugin we loaded al missed data
nsClientV3Plugin.initialLoadFinished = true
val from = dateUtil.now() - T.mins(7).msecs() val from = dateUtil.now() - T.mins(7).msecs()
val deviceStatuses = nsAndroidClient.getDeviceStatusModifiedSince(from) val deviceStatuses = nsAndroidClient.getDeviceStatusModifiedSince(from)
aapsLogger.debug("DEVICESTATUSES: $deviceStatuses") aapsLogger.debug("DEVICESTATUSES: $deviceStatuses")
if (deviceStatuses.isNotEmpty()) { if (deviceStatuses.isNotEmpty()) {
rxBus.send(EventNSClientNewLog("RCV", "${deviceStatuses.size} DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}")) rxBus.send(EventNSClientNewLog("RCV", "${deviceStatuses.size} DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
nsDeviceStatusHandler.handleNewData(deviceStatuses.toTypedArray()) nsDeviceStatusHandler.handleNewData(deviceStatuses.toTypedArray())
rxBus.send(EventNSClientNewLog("DONE DS", "")) rxBus.send(EventNSClientNewLog("DONE PROCESSING DS", ""))
} else { } else {
rxBus.send(EventNSClientNewLog("RCV END", "No DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}")) rxBus.send(EventNSClientNewLog("◄ RCV DS END", "No data from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
} }
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .enqueueUniqueWork(
@ -51,7 +54,7 @@ class LoadDeviceStatusWorker(
) )
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage nsClientV3Plugin.lastOperationError = error.localizedMessage
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -40,7 +40,7 @@ class LoadFoodsWorker(
if (nsClientV3Plugin.lastLoadedSrvModified.collections.foods++ % 5 == 0L) { if (nsClientV3Plugin.lastLoadedSrvModified.collections.foods++ % 5 == 0L) {
val foods: List<NSFood> = nsAndroidClient.getFoods(1000).values val foods: List<NSFood> = nsAndroidClient.getFoods(1000).values
aapsLogger.debug(LTag.NSCLIENT, "FOODS: $foods") aapsLogger.debug(LTag.NSCLIENT, "FOODS: $foods")
rxBus.send(EventNSClientNewLog("RCV", "${foods.size} FOODs")) rxBus.send(EventNSClientNewLog("RCV", "${foods.size} FOODs"))
// Schedule processing of fetched data // Schedule processing of fetched data
WorkManager.getInstance(context) WorkManager.getInstance(context)
.beginUniqueWork( .beginUniqueWork(
@ -53,7 +53,7 @@ class LoadFoodsWorker(
.then(OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build()) .then(OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build())
.enqueue() .enqueue()
} else { } else {
rxBus.send(EventNSClientNewLog("RCV", "FOOD skipped")) rxBus.send(EventNSClientNewLog("RCV FOOD", "skipped"))
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
@ -63,7 +63,7 @@ class LoadFoodsWorker(
} }
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage nsClientV3Plugin.lastOperationError = error.localizedMessage
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -27,7 +27,7 @@ class LoadLastModificationWorker(
aapsLogger.debug(LTag.NSCLIENT, "LAST MODIFIED: ${nsClientV3Plugin.newestDataOnServer}") aapsLogger.debug(LTag.NSCLIENT, "LAST MODIFIED: ${nsClientV3Plugin.newestDataOnServer}")
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Error: ", error) aapsLogger.error(LTag.NSCLIENT, "Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage nsClientV3Plugin.lastOperationError = error.localizedMessage
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -58,7 +58,7 @@ class LoadProfileStoreWorker(
{ nsClientV3Plugin.lastLoadedSrvModified.collections.profile = dateUtil.now() } { nsClientV3Plugin.lastLoadedSrvModified.collections.profile = dateUtil.now() }
nsClientV3Plugin.storeLastLoadedSrvModified() nsClientV3Plugin.storeLastLoadedSrvModified()
aapsLogger.debug(LTag.NSCLIENT, "PROFILE: $profile") aapsLogger.debug(LTag.NSCLIENT, "PROFILE: $profile")
rxBus.send(EventNSClientNewLog("RCV", "1 PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("RCV", "1 PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context) WorkManager.getInstance(context)
.beginUniqueWork( .beginUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
@ -69,7 +69,7 @@ class LoadProfileStoreWorker(
).then(OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build()) ).then(OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build())
.enqueue() .enqueue()
} else { } else {
rxBus.send(EventNSClientNewLog("RCV END", "No new PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("RCV PROFILE END", "No new data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
@ -78,7 +78,7 @@ class LoadProfileStoreWorker(
) )
} }
} else { } else {
rxBus.send(EventNSClientNewLog("RCV END", "No PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("◄ RCV PROFILE END", "No data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
@ -88,7 +88,7 @@ class LoadProfileStoreWorker(
} }
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -26,7 +26,7 @@ class LoadStatusWorker(
aapsLogger.debug(LTag.NSCLIENT, "STATUS: $status") aapsLogger.debug(LTag.NSCLIENT, "STATUS: $status")
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage nsClientV3Plugin.lastOperationError = error.localizedMessage
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -11,6 +11,7 @@ import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.core.utils.worker.then import info.nightscout.core.utils.worker.then
import info.nightscout.interfaces.nsclient.StoreDataForDb import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
@ -58,7 +59,7 @@ class LoadTreatmentsWorker(
aapsLogger.debug(LTag.NSCLIENT, "TREATMENTS: $treatments") aapsLogger.debug(LTag.NSCLIENT, "TREATMENTS: $treatments")
if (treatments.isNotEmpty()) { if (treatments.isNotEmpty()) {
val action = if (isFirstLoad) "RCV-FIRST" else "RCV" val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
rxBus.send(EventNSClientNewLog(action, "${treatments.size} TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("$action", "${treatments.size} TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
// Schedule processing of fetched data and continue of loading // Schedule processing of fetched data and continue of loading
val stopLoading = treatments.size != NSClientV3Plugin.RECORDS_TO_LOAD || response.code == 304 val stopLoading = treatments.size != NSClientV3Plugin.RECORDS_TO_LOAD || response.code == 304
WorkManager.getInstance(context) WorkManager.getInstance(context)
@ -66,7 +67,7 @@ class LoadTreatmentsWorker(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE, ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java) OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(response)) .setInputData(dataWorkerStorage.storeInputData(response.values))
.build() .build()
) )
// response 304 == Not modified (happens when date > srvModified => bad time on phone or server during upload // response 304 == Not modified (happens when date > srvModified => bad time on phone or server during upload
@ -79,14 +80,15 @@ class LoadTreatmentsWorker(
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded
nsClientV3Plugin.storeLastLoadedSrvModified() nsClientV3Plugin.storeLastLoadedSrvModified()
} }
rxBus.send(EventNSClientNewLog("RCV END", "No TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("◄ RCV TR END", "No data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb()
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .beginUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE, ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build() OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreTreatmentsWorker::class.java).build()
) )
.then(OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build())
.enqueue()
} }
} else { } else {
// End first load // End first load
@ -94,18 +96,19 @@ class LoadTreatmentsWorker(
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded
nsClientV3Plugin.storeLastLoadedSrvModified() nsClientV3Plugin.storeLastLoadedSrvModified()
} }
rxBus.send(EventNSClientNewLog("RCV END", "No new TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) rxBus.send(EventNSClientNewLog("◄ RCV TR END", "No new data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb()
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .beginUniqueWork(
nsClientV3Plugin.JOB_NAME, nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE, ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build() OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreTreatmentsWorker::class.java).build()
) )
.then(OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build())
.enqueue()
} }
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage nsClientV3Plugin.lastOperationError = error.localizedMessage
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -72,7 +72,7 @@ class ProcessFoodWorker(
storeDataForDb.foods.addAll(foods) storeDataForDb.foods.addAll(foods)
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }

View file

@ -24,7 +24,6 @@ import info.nightscout.plugins.sync.nsclientV3.extensions.toTherapyEvent
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.treatment.NSBolus import info.nightscout.sdk.localmodel.treatment.NSBolus
import info.nightscout.sdk.localmodel.treatment.NSBolusWizard import info.nightscout.sdk.localmodel.treatment.NSBolusWizard
import info.nightscout.sdk.localmodel.treatment.NSCarbs import info.nightscout.sdk.localmodel.treatment.NSCarbs
@ -57,12 +56,12 @@ class ProcessTreatmentsWorker(
override suspend fun doWorkAndLog(): Result { override suspend fun doWorkAndLog(): Result {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as NSAndroidClient.ReadResponse<List<NSTreatment>>? val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as List<NSTreatment>?
?: return Result.failure(workDataOf("Error" to "missing input data")) ?: return Result.failure(workDataOf("Error" to "missing input data"))
try { try {
var latestDateInReceivedData: Long = 0 var latestDateInReceivedData: Long = 0
for (treatment in treatments.values) { for (treatment in treatments) {
aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment") aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment")
val date = treatment.date ?: continue val date = treatment.date ?: continue
if (date > latestDateInReceivedData) latestDateInReceivedData = date if (date > latestDateInReceivedData) latestDateInReceivedData = date
@ -139,7 +138,7 @@ class ProcessTreatmentsWorker(
// xDripBroadcast.sendTreatments(treatments) // xDripBroadcast.sendTreatments(treatments)
} catch (error: Exception) { } catch (error: Exception) {
aapsLogger.error("Error: ", error) aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
return Result.failure(workDataOf("Error" to error.localizedMessage)) return Result.failure(workDataOf("Error" to error.localizedMessage))
} }
return Result.success() return Result.success()

View file

@ -2,9 +2,14 @@ package info.nightscout.plugins.sync.tidepool
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ScrollView import android.widget.ScrollView
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
@ -15,12 +20,13 @@ import info.nightscout.plugins.sync.tidepool.events.EventTidepoolResetData
import info.nightscout.plugins.sync.tidepool.events.EventTidepoolUpdateGUI import info.nightscout.plugins.sync.tidepool.events.EventTidepoolUpdateGUI
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class TidepoolFragment : DaggerFragment() { class TidepoolFragment : DaggerFragment(), MenuProvider {
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var tidepoolPlugin: TidepoolPlugin @Inject lateinit var tidepoolPlugin: TidepoolPlugin
@ -28,6 +34,15 @@ class TidepoolFragment : DaggerFragment() {
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rh: ResourceHelper
companion object {
const val ID_MENU_LOGIN = 530
const val ID_MENU_SEND_NOW = 531
const val ID_MENU_REMOVE_ALL = 532
const val ID_MENU_FULL_SYNC = 533
}
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
@ -39,15 +54,41 @@ class TidepoolFragment : DaggerFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = TidepoolFragmentBinding.inflate(inflater, container, false) _binding = TidepoolFragmentBinding.inflate(inflater, container, false)
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
return binding.root return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
super.onViewCreated(view, savedInstanceState) menu.add(Menu.FIRST, ID_MENU_LOGIN, 0, rh.gs(info.nightscout.core.ui.R.string.login)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
binding.login.setOnClickListener { tidepoolUploader.doLogin(false) } menu.add(Menu.FIRST, ID_MENU_SEND_NOW, 0, rh.gs(R.string.upload_now)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
binding.uploadnow.setOnClickListener { rxBus.send(EventTidepoolDoUpload()) } menu.add(Menu.FIRST, ID_MENU_REMOVE_ALL, 0, rh.gs(R.string.remove_all)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
binding.removeall.setOnClickListener { rxBus.send(EventTidepoolResetData()) } menu.add(Menu.FIRST, ID_MENU_FULL_SYNC, 0, rh.gs(R.string.full_sync)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
binding.resertstart.setOnClickListener { sp.putLong(R.string.key_tidepool_last_end, 0) } menu.setGroupDividerEnabled(true)
}
override fun onMenuItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
ID_MENU_LOGIN -> {
tidepoolUploader.doLogin(false)
true
}
ID_MENU_SEND_NOW -> {
rxBus.send(EventTidepoolDoUpload())
true
}
ID_MENU_REMOVE_ALL -> {
rxBus.send(EventTidepoolResetData())
true
}
ID_MENU_FULL_SYNC -> {
sp.putLong(R.string.key_tidepool_last_end, 0)
true
}
else -> false
} }
@Synchronized @Synchronized
@ -56,14 +97,16 @@ class TidepoolFragment : DaggerFragment() {
disposable += rxBus disposable += rxBus
.toObservable(EventTidepoolUpdateGUI::class.java) .toObservable(EventTidepoolUpdateGUI::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({ updateGui() }, fabricPrivacy::logException)
if (_binding == null) return@subscribe updateGui()
}
private fun updateGui() {
tidepoolPlugin.updateLog() tidepoolPlugin.updateLog()
binding.log.text = tidepoolPlugin.textLog _binding?.log?.text = tidepoolPlugin.textLog
binding.status.text = tidepoolUploader.connectionStatus.name _binding?.status?.text = tidepoolUploader.connectionStatus.name
binding.log.text = tidepoolPlugin.textLog _binding?.log?.text = tidepoolPlugin.textLog
binding.logscrollview.fullScroll(ScrollView.FOCUS_DOWN) _binding?.logScrollview?.fullScroll(ScrollView.FOCUS_DOWN)
}, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
@ -77,5 +120,4 @@ class TidepoolFragment : DaggerFragment() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
} }

View file

@ -10,11 +10,12 @@ import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.receivers.ReceiverStatusStore
import info.nightscout.interfaces.sync.Sync import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HtmlHelper import info.nightscout.interfaces.utils.HtmlHelper
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged
import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.tidepool.comm.TidepoolUploader import info.nightscout.plugins.sync.tidepool.comm.TidepoolUploader
import info.nightscout.plugins.sync.tidepool.comm.UploadChunk import info.nightscout.plugins.sync.tidepool.comm.UploadChunk
import info.nightscout.plugins.sync.tidepool.events.EventTidepoolDoUpload import info.nightscout.plugins.sync.tidepool.events.EventTidepoolDoUpload
@ -24,7 +25,7 @@ import info.nightscout.plugins.sync.tidepool.events.EventTidepoolUpdateGUI
import info.nightscout.plugins.sync.tidepool.utils.RateLimit import info.nightscout.plugins.sync.tidepool.utils.RateLimit
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNetworkChange import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNewBG import info.nightscout.rx.events.EventNewBG
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
@ -50,7 +51,7 @@ class TidepoolPlugin @Inject constructor(
private val uploadChunk: UploadChunk, private val uploadChunk: UploadChunk,
private val sp: SP, private val sp: SP,
private val rateLimit: RateLimit, private val rateLimit: RateLimit,
private val receiverStatusStore: ReceiverStatusStore, private val receiverDelegate: ReceiverDelegate,
private val uiInteraction: UiInteraction private val uiInteraction: UiInteraction
) : Sync, PluginBase( ) : Sync, PluginBase(
PluginDescription() PluginDescription()
@ -67,10 +68,18 @@ class TidepoolPlugin @Inject constructor(
private val listLog = ArrayList<EventTidepoolStatus>() private val listLog = ArrayList<EventTidepoolStatus>()
var textLog: Spanned = HtmlHelper.fromHtml("") var textLog: Spanned = HtmlHelper.fromHtml("")
private val isAllowed get() = receiverDelegate.allowed
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
disposable += rxBus
.toObservable(EventConnectivityOptionChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev ->
rxBus.send(EventNSClientNewLog("● CONNECTIVITY", ev.blockingReason))
tidepoolUploader.resetInstance()
if (isAllowed) doUpload()
}, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventTidepoolDoUpload::class.java) .toObservable(EventTidepoolDoUpload::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -94,17 +103,13 @@ class TidepoolPlugin @Inject constructor(
disposable += rxBus disposable += rxBus
.toObservable(EventNewBG::class.java) .toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.filter { it.glucoseValueTimestamp != null } // better would be optional in API level >24 .subscribe({
.map { it.glucoseValueTimestamp!! } it.glucoseValueTimestamp?.let { bgReadingTimestamp ->
.subscribe({ bgReadingTimestamp ->
if (bgReadingTimestamp < uploadChunk.getLastEnd()) if (bgReadingTimestamp < uploadChunk.getLastEnd())
uploadChunk.setLastEnd(bgReadingTimestamp) uploadChunk.setLastEnd(bgReadingTimestamp)
if (isEnabled() if (isAllowed && rateLimit.rateLimit("tidepool-new-data-upload", T.mins(4).secs().toInt()))
&& (!sp.getBoolean(R.string.key_tidepool_only_while_charging, false) || receiverStatusStore.isCharging)
&& (!sp.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || receiverStatusStore.isWifiConnected)
&& rateLimit.rateLimit("tidepool-new-data-upload", T.mins(4).secs().toInt())
)
doUpload() doUpload()
}
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
@ -116,11 +121,6 @@ class TidepoolPlugin @Inject constructor(
) )
tidepoolUploader.resetInstance() tidepoolUploader.resetInstance()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNetworkChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({}, fabricPrivacy::logException) // TODO start upload on wifi connect
} }
override fun onStop() { override fun onStop() {

View file

@ -8,7 +8,7 @@ import okhttp3.Response
import okio.Buffer import okio.Buffer
import java.io.IOException import java.io.IOException
class InfoInterceptor(val tag: String = "interceptor", val aapsLogger: AAPSLogger) : Interceptor { class InfoInterceptor(val aapsLogger: AAPSLogger) : Interceptor {
@Throws(IOException::class) @Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {

View file

@ -4,38 +4,37 @@ import info.nightscout.plugins.sync.tidepool.messages.AuthReplyMessage
import info.nightscout.plugins.sync.tidepool.messages.DatasetReplyMessage import info.nightscout.plugins.sync.tidepool.messages.DatasetReplyMessage
import okhttp3.Headers import okhttp3.Headers
class Session(val authHeader: String?, class Session(
val authHeader: String?,
private val sessionTokenHeader: String, private val sessionTokenHeader: String,
val service: TidepoolApiService?) { val service: TidepoolApiService?
) {
internal var token: String? = null internal var token: String? = null
internal var authReply: AuthReplyMessage? = null internal var authReply: AuthReplyMessage? = null
internal var datasetReply: DatasetReplyMessage? = null internal var datasetReply: DatasetReplyMessage? = null
internal var start: Long = 0 internal var start: Long = 0
internal var end: Long = 0 internal var end: Long = 0
@Volatile @Volatile
internal var iterations: Int = 0 internal var iterations: Int = 0
fun populateHeaders(headers: Headers) { fun populateHeaders(headers: Headers) {
if (this.token == null) { if (this.token == null) {
this.token = headers.get(sessionTokenHeader) this.token = headers[sessionTokenHeader]
} }
} }
fun populateBody(obj: Any?) { fun populateBody(obj: Any?) {
if (obj == null) return when (obj) {
if (obj is AuthReplyMessage) { is AuthReplyMessage -> authReply = obj
authReply = obj
} else if (obj is List<*>) {
val list = obj as? List<*>?
list?.getOrNull(0)?.let { is List<*> ->
if (it is DatasetReplyMessage) { (obj as? List<*>?)?.getOrNull(0)?.let {
datasetReply = it if (it is DatasetReplyMessage) datasetReply = it
} }
}
} else if (obj is DatasetReplyMessage) { is DatasetReplyMessage -> datasetReply = obj
datasetReply = obj
} }
} }
} }

View file

@ -8,7 +8,14 @@ import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
internal class TidepoolCallback<T>(private val aapsLogger: AAPSLogger, private val rxBus: RxBus, private val session: Session, val name: String, val onSuccess: () -> Unit, val onFail: () -> Unit) : internal class TidepoolCallback<T>(
private val aapsLogger: AAPSLogger,
private val rxBus: RxBus,
private val session: Session,
private val name: String,
private val onSuccess: () -> Unit,
private val onFail: () -> Unit
) :
Callback<T> { Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) { override fun onResponse(call: Call<T>, response: Response<T>) {

View file

@ -5,8 +5,8 @@ import android.os.PowerManager
import android.os.SystemClock import android.os.SystemClock
import info.nightscout.core.ui.dialogs.OKDialog import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.tidepool.events.EventTidepoolStatus import info.nightscout.plugins.sync.tidepool.events.EventTidepoolStatus
import info.nightscout.plugins.sync.tidepool.messages.AuthReplyMessage import info.nightscout.plugins.sync.tidepool.messages.AuthReplyMessage
import info.nightscout.plugins.sync.tidepool.messages.AuthRequestMessage import info.nightscout.plugins.sync.tidepool.messages.AuthRequestMessage
@ -37,11 +37,12 @@ class TidepoolUploader @Inject constructor(
private val rh: ResourceHelper, private val rh: ResourceHelper,
private val sp: SP, private val sp: SP,
private val uploadChunk: UploadChunk, private val uploadChunk: UploadChunk,
private val activePlugin: ActivePlugin,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val receiverDelegate: ReceiverDelegate,
private val config: Config private val config: Config
) { ) {
private val isAllowed get() = receiverDelegate.allowed
private var wl: PowerManager.WakeLock? = null private var wl: PowerManager.WakeLock? = null
companion object { companion object {
@ -57,7 +58,7 @@ class TidepoolUploader @Inject constructor(
private var session: Session? = null private var session: Session? = null
enum class ConnectionStatus { enum class ConnectionStatus {
DISCONNECTED, CONNECTING, CONNECTED, FAILED BLOCKED, DISCONNECTED, CONNECTING, CONNECTED, FAILED
} }
var connectionStatus: ConnectionStatus = ConnectionStatus.DISCONNECTED var connectionStatus: ConnectionStatus = ConnectionStatus.DISCONNECTED
@ -70,7 +71,7 @@ class TidepoolUploader @Inject constructor(
val client = OkHttpClient.Builder() val client = OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor) .addInterceptor(httpLoggingInterceptor)
.addInterceptor(InfoInterceptor(TidepoolUploader::class.java.name, aapsLogger)) .addInterceptor(InfoInterceptor(aapsLogger))
.build() .build()
retrofit = Retrofit.Builder() retrofit = Retrofit.Builder()
@ -87,7 +88,6 @@ class TidepoolUploader @Inject constructor(
return Session(AuthRequestMessage.getAuthRequestHeader(sp), SESSION_TOKEN_HEADER, service) return Session(AuthRequestMessage.getAuthRequestHeader(sp), SESSION_TOKEN_HEADER, service)
} }
// TODO: call on preference change
fun resetInstance() { fun resetInstance() {
retrofit = null retrofit = null
aapsLogger.debug(LTag.TIDEPOOL, "Instance reset") aapsLogger.debug(LTag.TIDEPOOL, "Instance reset")
@ -96,6 +96,11 @@ class TidepoolUploader @Inject constructor(
@Synchronized @Synchronized
fun doLogin(doUpload: Boolean = false) { fun doLogin(doUpload: Boolean = false) {
if (!isAllowed) {
connectionStatus = ConnectionStatus.BLOCKED
aapsLogger.debug(LTag.TIDEPOOL, "Blocked by connectivity settings")
return
}
if (connectionStatus == ConnectionStatus.CONNECTED || connectionStatus == ConnectionStatus.CONNECTING) { if (connectionStatus == ConnectionStatus.CONNECTED || connectionStatus == ConnectionStatus.CONNECTING) {
aapsLogger.debug(LTag.TIDEPOOL, "Already connected") aapsLogger.debug(LTag.TIDEPOOL, "Already connected")
return return
@ -109,12 +114,15 @@ class TidepoolUploader @Inject constructor(
rxBus.send(EventTidepoolStatus(("Connecting"))) rxBus.send(EventTidepoolStatus(("Connecting")))
val call = session?.service?.getLogin(authHeader) val call = session?.service?.getLogin(authHeader)
call?.enqueue(TidepoolCallback<AuthReplyMessage>(aapsLogger, rxBus, session!!, "Login", { call?.enqueue(TidepoolCallback<AuthReplyMessage>(
aapsLogger, rxBus, session!!, "Login",
{
startSession(session!!, doUpload) startSession(session!!, doUpload)
}, { }, {
connectionStatus = ConnectionStatus.FAILED connectionStatus = ConnectionStatus.FAILED
releaseWakeLock() releaseWakeLock()
})) })
)
return return
} else { } else {
aapsLogger.debug(LTag.TIDEPOOL, "Cannot do login as user credentials have not been set correctly") aapsLogger.debug(LTag.TIDEPOOL, "Cannot do login as user credentials have not been set correctly")
@ -130,16 +138,14 @@ class TidepoolUploader @Inject constructor(
session.authHeader?.let { session.authHeader?.let {
val call = session.service?.getLogin(it) val call = session.service?.getLogin(it)
call?.enqueue(TidepoolCallback<AuthReplyMessage>(aapsLogger, rxBus, session, "Login", { call?.enqueue(TidepoolCallback<AuthReplyMessage>(
aapsLogger, rxBus, session, "Login",
{
OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Successfully logged into Tidepool.") OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Successfully logged into Tidepool.")
}, { }, {
OKDialog.show( OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Failed to log into Tidepool.\nCheck that your user name and password are correct.")
rootContext, })
rh.gs(R.string.tidepool),
"Failed to log into Tidepool.\nCheck that your user name and password are correct."
) )
}))
} }
?: OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Cannot do login as user credentials have not been set correctly") ?: OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Cannot do login as user credentials have not been set correctly")
@ -154,24 +160,26 @@ class TidepoolUploader @Inject constructor(
session.authReply!!.userid!!, "AAPS", 1 session.authReply!!.userid!!, "AAPS", 1
) )
datasetCall.enqueue(TidepoolCallback<List<DatasetReplyMessage>>(aapsLogger, rxBus, session, "Get Open Datasets", { datasetCall.enqueue(
TidepoolCallback<List<DatasetReplyMessage>>(
aapsLogger, rxBus, session, "Get Open Datasets",
{
if (session.datasetReply == null) { if (session.datasetReply == null) {
rxBus.send(EventTidepoolStatus(("Creating new dataset"))) rxBus.send(EventTidepoolStatus(("Creating new dataset")))
val call = session.service.openDataSet( val call = session.service.openDataSet(session.token!!, session.authReply!!.userid!!, OpenDatasetRequestMessage(config, dateUtil).getBody())
session.token!!, session.authReply!!.userid!!, call.enqueue(TidepoolCallback<DatasetReplyMessage>(
OpenDatasetRequestMessage(config, dateUtil).getBody() aapsLogger, rxBus, session, "Open New Dataset",
) {
call.enqueue(TidepoolCallback<DatasetReplyMessage>(aapsLogger, rxBus, session, "Open New Dataset", {
connectionStatus = ConnectionStatus.CONNECTED connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("New dataset OK"))) rxBus.send(EventTidepoolStatus(("New dataset OK")))
if (doUpload) doUpload() if (doUpload) doUpload()
else else releaseWakeLock()
releaseWakeLock()
}, { }, {
rxBus.send(EventTidepoolStatus(("New dataset FAILED"))) rxBus.send(EventTidepoolStatus(("New dataset FAILED")))
connectionStatus = ConnectionStatus.FAILED connectionStatus = ConnectionStatus.FAILED
releaseWakeLock() releaseWakeLock()
})) })
)
} else { } else {
aapsLogger.debug(LTag.TIDEPOOL, "Existing Dataset: " + session.datasetReply!!.getUploadId()) aapsLogger.debug(LTag.TIDEPOOL, "Existing Dataset: " + session.datasetReply!!.getUploadId())
// TODO: Wouldn't need to do this if we could block on the above `call.enqueue`. // TODO: Wouldn't need to do this if we could block on the above `call.enqueue`.
@ -179,14 +187,14 @@ class TidepoolUploader @Inject constructor(
connectionStatus = ConnectionStatus.CONNECTED connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("Appending to existing dataset"))) rxBus.send(EventTidepoolStatus(("Appending to existing dataset")))
if (doUpload) doUpload() if (doUpload) doUpload()
else else releaseWakeLock()
releaseWakeLock()
} }
}, { }, {
connectionStatus = ConnectionStatus.FAILED connectionStatus = ConnectionStatus.FAILED
rxBus.send(EventTidepoolStatus(("Open dataset FAILED"))) rxBus.send(EventTidepoolStatus(("Open dataset FAILED")))
releaseWakeLock() releaseWakeLock()
})) })
)
} else { } else {
aapsLogger.error("Got login response but cannot determine userId - cannot proceed") aapsLogger.error("Got login response but cannot determine userId - cannot proceed")
connectionStatus = ConnectionStatus.FAILED connectionStatus = ConnectionStatus.FAILED
@ -197,6 +205,11 @@ class TidepoolUploader @Inject constructor(
@Synchronized @Synchronized
fun doUpload() { fun doUpload() {
if (!isAllowed) {
connectionStatus = ConnectionStatus.BLOCKED
aapsLogger.debug(LTag.TIDEPOOL, "Blocked by connectivity settings")
return
}
session.let { session -> session.let { session ->
if (session == null) { if (session == null) {
aapsLogger.error("Session is null, cannot proceed") aapsLogger.error("Session is null, cannot proceed")
@ -225,16 +238,20 @@ class TidepoolUploader @Inject constructor(
rxBus.send(EventTidepoolStatus(("Uploading"))) rxBus.send(EventTidepoolStatus(("Uploading")))
if (session.service != null && session.token != null && session.datasetReply != null) { if (session.service != null && session.token != null && session.datasetReply != null) {
val call = session.service.doUpload(session.token!!, session.datasetReply!!.getUploadId()!!, body) val call = session.service.doUpload(session.token!!, session.datasetReply!!.getUploadId()!!, body)
call.enqueue(TidepoolCallback<UploadReplyMessage>(aapsLogger, rxBus, session, "Data Upload", { call.enqueue(TidepoolCallback<UploadReplyMessage>(
aapsLogger, rxBus, session, "Data Upload",
{
uploadChunk.setLastEnd(session.end) uploadChunk.setLastEnd(session.end)
connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("Upload completed OK"))) rxBus.send(EventTidepoolStatus(("Upload completed OK")))
releaseWakeLock() releaseWakeLock()
uploadNext() uploadNext()
}, { }, {
connectionStatus = ConnectionStatus.FAILED connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Upload FAILED"))) rxBus.send(EventTidepoolStatus(("Upload FAILED")))
releaseWakeLock() releaseWakeLock()
})) })
)
} }
} }
} }
@ -242,7 +259,12 @@ class TidepoolUploader @Inject constructor(
} }
private fun uploadNext() { private fun uploadNext() {
if (uploadChunk.getLastEnd() < dateUtil.now() - T.mins(1).msecs()) { if (!isAllowed) {
connectionStatus = ConnectionStatus.BLOCKED
aapsLogger.debug(LTag.TIDEPOOL, "Blocked by connectivity settings")
return
}
if (uploadChunk.getLastEnd() < dateUtil.now() - T.hours(3).msecs() - T.mins(1).msecs()) {
SystemClock.sleep(3000) SystemClock.sleep(3000)
aapsLogger.debug(LTag.TIDEPOOL, "Restarting doUpload. Last: " + dateUtil.dateAndTimeString(uploadChunk.getLastEnd())) aapsLogger.debug(LTag.TIDEPOOL, "Restarting doUpload. Last: " + dateUtil.dateAndTimeString(uploadChunk.getLastEnd()))
doUpload() doUpload()
@ -253,7 +275,9 @@ class TidepoolUploader @Inject constructor(
if (session?.datasetReply?.id != null) { if (session?.datasetReply?.id != null) {
extendWakeLock(60000) extendWakeLock(60000)
val call = session!!.service?.deleteDataSet(session!!.token!!, session!!.datasetReply!!.id!!) val call = session!!.service?.deleteDataSet(session!!.token!!, session!!.datasetReply!!.id!!)
call?.enqueue(TidepoolCallback(aapsLogger, rxBus, session!!, "Delete Dataset", { call?.enqueue(TidepoolCallback(
aapsLogger, rxBus, session!!, "Delete Dataset",
{
connectionStatus = ConnectionStatus.DISCONNECTED connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Dataset removed OK"))) rxBus.send(EventTidepoolStatus(("Dataset removed OK")))
releaseWakeLock() releaseWakeLock()
@ -261,7 +285,8 @@ class TidepoolUploader @Inject constructor(
connectionStatus = ConnectionStatus.DISCONNECTED connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Dataset remove FAILED"))) rxBus.send(EventTidepoolStatus(("Dataset remove FAILED")))
releaseWakeLock() releaseWakeLock()
})) })
)
} else { } else {
aapsLogger.error("Got login response but cannot determine datasetId - cannot proceed") aapsLogger.error("Got login response but cannot determine datasetId - cannot proceed")
} }
@ -278,7 +303,10 @@ class TidepoolUploader @Inject constructor(
requireNotNull(userId) requireNotNull(userId)
extendWakeLock(60000) extendWakeLock(60000)
val call = session.service?.deleteAllData(token, userId) val call = session.service?.deleteAllData(token, userId)
call?.enqueue(TidepoolCallback(aapsLogger, rxBus, session, "Delete all data", { call?.enqueue(
TidepoolCallback(
aapsLogger, rxBus, session, "Delete all data",
{
connectionStatus = ConnectionStatus.DISCONNECTED connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("All data removed OK"))) rxBus.send(EventTidepoolStatus(("All data removed OK")))
releaseWakeLock() releaseWakeLock()
@ -286,7 +314,8 @@ class TidepoolUploader @Inject constructor(
connectionStatus = ConnectionStatus.DISCONNECTED connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("All data remove FAILED"))) rxBus.send(EventTidepoolStatus(("All data remove FAILED")))
releaseWakeLock() releaseWakeLock()
})) })
)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
aapsLogger.error("Got login response but cannot determine userId - cannot proceed") aapsLogger.error("Got login response but cannot determine userId - cannot proceed")
} }
@ -307,7 +336,7 @@ class TidepoolUploader @Inject constructor(
@Synchronized @Synchronized
private fun releaseWakeLock() { private fun releaseWakeLock() {
wl?.let { wl?.let {
if (it.isHeld) { if (it.isHeld)
try { try {
it.release() it.release()
} catch (e: Exception) { } catch (e: Exception) {
@ -315,6 +344,5 @@ class TidepoolUploader @Inject constructor(
} }
} }
} }
}
} }

View file

@ -41,21 +41,21 @@ class UploadChunk @Inject constructor(
private val maxUploadSize = T.days(7).msecs() // don't change this private val maxUploadSize = T.days(7).msecs() // don't change this
fun getNext(session: Session?): String? { fun getNext(session: Session?): String? {
if (session == null) session ?: return null
return null
session.start = getLastEnd() session.start = getLastEnd()
session.end = min(session.start + maxUploadSize, dateUtil.now()) // do not upload last 3h, TBR can be still running
session.end = min(session.start + maxUploadSize, dateUtil.now() - T.hours(3).msecs())
val result = get(session.start, session.end) val result = get(session.start, session.end)
if (result.length < 3) { if (result.length < 3) {
aapsLogger.debug(LTag.TIDEPOOL, "No records in this time period, setting start to best end time") aapsLogger.debug(LTag.TIDEPOOL, "No records in this time period, setting start to best end time")
setLastEnd(max(session.end, getOldestRecordTimeStamp())) setLastEnd(session.end)
} }
return result return result
} }
operator fun get(start: Long, end: Long): String { fun get(start: Long, end: Long): String {
aapsLogger.debug(LTag.TIDEPOOL, "Syncing data between: " + dateUtil.dateAndTimeString(start) + " -> " + dateUtil.dateAndTimeString(end)) aapsLogger.debug(LTag.TIDEPOOL, "Syncing data between: " + dateUtil.dateAndTimeString(start) + " -> " + dateUtil.dateAndTimeString(end))
if (end <= start) { if (end <= start) {
@ -69,15 +69,10 @@ class UploadChunk @Inject constructor(
val records = LinkedList<BaseElement>() val records = LinkedList<BaseElement>()
if (sp.getBoolean(R.string.key_tidepool_upload_bolus, true))
records.addAll(getTreatments(start, end)) records.addAll(getTreatments(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_bg, true))
records.addAll(getBloodTests(start, end)) records.addAll(getBloodTests(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_tbr, true))
records.addAll(getBasals(start, end)) records.addAll(getBasals(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_cgm, true))
records.addAll(getBgReadings(start, end)) records.addAll(getBgReadings(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_profile, true))
records.addAll(getProfiles(start, end)) records.addAll(getProfiles(start, end))
return GsonInstance.defaultGsonInstance().toJson(records) return GsonInstance.defaultGsonInstance().toJson(records)
@ -99,21 +94,6 @@ class UploadChunk @Inject constructor(
} }
} }
// numeric limits must match max time windows
private fun getOldestRecordTimeStamp(): Long {
// TODO we could make sure we include records older than the first bg record for completeness
val start: Long = 0
val end = dateUtil.now()
val bgReadingList = repository.compatGetBgReadingsDataFromTime(start, end, true)
.blockingGet()
return if (bgReadingList.isNotEmpty())
bgReadingList[0].timestamp
else -1
}
private fun getTreatments(start: Long, end: Long): List<BaseElement> { private fun getTreatments(start: Long, end: Long): List<BaseElement> {
val result = LinkedList<BaseElement>() val result = LinkedList<BaseElement>()
repository.getBolusesDataFromTimeToTime(start, end, true) repository.getBolusesDataFromTimeToTime(start, end, true)

View file

@ -7,7 +7,7 @@ import info.nightscout.interfaces.profile.Profile
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import java.util.UUID import java.util.UUID
class BasalElement(tbr: TemporaryBasal, private val profile: Profile, dateUtil: DateUtil) class BasalElement(tbr: TemporaryBasal, profile: Profile, dateUtil: DateUtil)
: BaseElement(tbr.timestamp, UUID.nameUUIDFromBytes(("AAPS-basal" + tbr.timestamp).toByteArray()).toString(), dateUtil) { : BaseElement(tbr.timestamp, UUID.nameUUIDFromBytes(("AAPS-basal" + tbr.timestamp).toByteArray()).toString(), dateUtil) {
internal var timestamp: Long = 0 // not exposed internal var timestamp: Long = 0 // not exposed

View file

@ -35,14 +35,21 @@ class ProfileElement(ps: EffectiveProfileSwitch, serialNumber: String, dateUtil:
init { init {
type = "pumpSettings" type = "pumpSettings"
val profile: Profile = ProfileSealed.EPS(ps) val profile: Profile = ProfileSealed.EPS(ps)
for (br in profile.getBasalValues()) // for (br in profile.getBasalValues())
basalSchedules.Normal.add(BasalRate(br.timeAsSeconds * 1000, br.value)) // basalSchedules.Normal.add(BasalRate(br.timeAsSeconds * 1000, br.value))
for (target in profile.getSingleTargetsMgdl()) // for (target in profile.getSingleTargetsMgdl())
bgTargets.Normal.add(Target(target.timeAsSeconds * 1000, target.value.toInt())) // bgTargets.Normal.add(Target(target.timeAsSeconds * 1000, target.value.toInt()))
for (ic in profile.getIcsValues()) // for (ic in profile.getIcsValues())
carbRatios.Normal.add(Ratio(ic.timeAsSeconds * 1000, ic.value.toInt())) // carbRatios.Normal.add(Ratio(ic.timeAsSeconds * 1000, ic.value.toInt()))
for (isf in profile.getIsfsMgdlValues()) // for (isf in profile.getIsfsMgdlValues())
insulinSensitivities.Normal.add(Ratio(isf.timeAsSeconds * 1000, isf.value.toInt())) // insulinSensitivities.Normal.add(Ratio(isf.timeAsSeconds * 1000, isf.value.toInt()))
for (hour in 0..23) {
val seconds = hour * 3600
basalSchedules.Normal.add(BasalRate(seconds * 1000, profile.getBasalTimeFromMidnight(seconds)))
bgTargets.Normal.add(Target(seconds * 1000, Profile.toMgdl((((profile.getTargetLowMgdlTimeFromMidnight(seconds) + profile.getTargetLowMgdlTimeFromMidnight(seconds))) / 2)).toInt()))
carbRatios.Normal.add(Ratio(seconds * 1000, profile.getIcTimeFromMidnight(seconds).toInt()))
insulinSensitivities.Normal.add(Ratio(seconds * 1000, profile.getIsfMgdlTimeFromMidnight(seconds).toInt()))
}
} }
inner class BasalProfile internal constructor( inner class BasalProfile internal constructor(

View file

@ -3,7 +3,7 @@
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=".sync.nsShared.NSClientFragment"> tools:context="info.nightscout.plugins.sync.nsShared.NSClientFragment">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,82 +1,46 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
tools:context="info.nightscout.plugins.sync.tidepool.TidepoolFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="@string/status"
tools:ignore="RtlHardcoded" />
<TextView <TextView
android:id="@+id/status" android:id="@+id/status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginTop="32dp"
android:text="-"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<com.google.android.material.button.MaterialButton
android:id="@+id/login"
style="@style/GrayButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:textStyle="normal|bold" />
android:text="@string/login"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status" />
<com.google.android.material.button.MaterialButton </LinearLayout>
android:id="@+id/uploadnow"
style="@style/GrayButton"
android:layout_width="113dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:text="@string/upload_now"
app:layout_constraintStart_toEndOf="@+id/login"
app:layout_constraintTop_toBottomOf="@+id/status" />
<com.google.android.material.button.MaterialButton
android:id="@+id/removeall"
style="@style/GrayButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="8dp"
android:text="@string/remove_all"
app:layout_constraintStart_toEndOf="@+id/uploadnow"
app:layout_constraintTop_toBottomOf="@+id/status" />
<com.google.android.material.button.MaterialButton
android:id="@+id/resertstart"
style="@style/GrayButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="8dp"
android:text="@string/reset_start"
app:layout_constraintStart_toEndOf="@+id/removeall"
app:layout_constraintTop_toBottomOf="@+id/status" />
<ScrollView <ScrollView
android:id="@+id/logscrollview" android:id="@+id/log_scrollview"
android:layout_width="0dp" android:layout_width="fill_parent"
android:layout_height="0dp" android:layout_height="fill_parent"
android:layout_marginStart="8dp" android:layout_marginStart="5dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="5dp">
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/login"
app:layout_constraintVertical_bias="0.023">
<TextView <TextView
android:id="@+id/log" android:id="@+id/log"
android:layout_width="match_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="-- logs --" android:text="" />
tools:ignore="HardcodedText" />
</ScrollView> </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>

View file

@ -11,7 +11,7 @@
<string name="key_ns_device_status_last_synced_id" translatable="false">ns_device_status_last_synced_id</string> <string name="key_ns_device_status_last_synced_id" translatable="false">ns_device_status_last_synced_id</string>
<string name="key_ns_temporary_basal_last_synced_id" translatable="false">ns_temporary_basal_last_synced_id</string> <string name="key_ns_temporary_basal_last_synced_id" translatable="false">ns_temporary_basal_last_synced_id</string>
<string name="key_ns_extended_bolus_last_synced_id" translatable="false">ns_extended_bolus_last_synced_id</string> <string name="key_ns_extended_bolus_last_synced_id" translatable="false">ns_extended_bolus_last_synced_id</string>
<string name="key_ns_profile_switch_last_synced_id" translatable="false">profile_switch_last_synced_id</string> <string name="key_ns_profile_switch_last_synced_id" translatable="false">ns_profile_switch_last_synced_id</string>
<string name="key_ns_effective_profile_switch_last_synced_id" translatable="false">ns_effective_profile_switch_last_synced_id</string> <string name="key_ns_effective_profile_switch_last_synced_id" translatable="false">ns_effective_profile_switch_last_synced_id</string>
<string name="key_ns_offline_event_last_synced_id" translatable="false">ns_offline_event_last_synced_id</string> <string name="key_ns_offline_event_last_synced_id" translatable="false">ns_offline_event_last_synced_id</string>
<string name="key_ns_profile_store_last_synced_timestamp" translatable="false">ns_profile_store_last_synced_timestamp</string> <string name="key_ns_profile_store_last_synced_timestamp" translatable="false">ns_profile_store_last_synced_timestamp</string>
@ -48,7 +48,7 @@
<!-- NSClient --> <!-- NSClient -->
<string name="key_ns_client_autoscroll" translatable="false">ns_client_autoscroll</string> <string name="key_ns_client_autoscroll" translatable="false">ns_client_autoscroll</string>
<string name="key_ns_client_paused" translatable="false">ns_client_paused</string> <string name="key_ns_paused" translatable="false">ns_client_paused</string>
<string name="key_ns_log_app_started_event" translatable="false">ns_log_app_started_event</string> <string name="key_ns_log_app_started_event" translatable="false">ns_log_app_started_event</string>
<string name="no_write_permission">NSCLIENT has no write permission. Wrong API secret?</string> <string name="no_write_permission">NSCLIENT has no write permission. Wrong API secret?</string>
@ -113,14 +113,7 @@
<string name="key_tidepool_password" translatable="false">tidepool_password</string> <string name="key_tidepool_password" translatable="false">tidepool_password</string>
<string name="key_tidepool_dev_servers" translatable="false">tidepool_dev_servers</string> <string name="key_tidepool_dev_servers" translatable="false">tidepool_dev_servers</string>
<string name="key_tidepool_test_login" translatable="false">tidepool_test_login</string> <string name="key_tidepool_test_login" translatable="false">tidepool_test_login</string>
<string name="key_tidepool_only_while_charging" translatable="false">tidepool_only_while_charging</string>
<string name="key_tidepool_only_while_unmetered" translatable="false">tidepool_only_while_unmetered</string>
<string name="key_tidepool_last_end" translatable="false">tidepool_last_end</string> <string name="key_tidepool_last_end" translatable="false">tidepool_last_end</string>
<string name="key_tidepool_upload_profile" translatable="false">tidepool_upload_profile</string>
<string name="key_tidepool_upload_tbr" translatable="false">tidepool_upload_tbr</string>
<string name="key_tidepool_upload_cgm" translatable="false">tidepool_upload_cgm</string>
<string name="key_tidepool_upload_bolus" translatable="false">tidepool_upload_bolus</string>
<string name="key_tidepool_upload_bg" translatable="false">tidepool_upload_bg</string>
<string name="summary_tidepool_username">Your Tidepool login user name, normally your email address</string> <string name="summary_tidepool_username">Your Tidepool login user name, normally your email address</string>
<string name="title_tidepool_username">Login User Name</string> <string name="title_tidepool_username">Login User Name</string>
@ -187,5 +180,7 @@
<!-- DataBroadcast--> <!-- DataBroadcast-->
<string name="data_broadcaster" translatable="false">Data Broadcaster</string> <string name="data_broadcaster" translatable="false">Data Broadcaster</string>
<string name="ns_use_ws_title">Connect to websockets</string>
<string name="ns_use_ws_summary">Enabling means: faster updates, receiving alarms and announcements and higher battery consumption similar to v1. All other uploaderds to NS must use v3 protocol.</string>
</resources> </resources>

View file

@ -178,14 +178,13 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="nsclient_advanced" android:key="nsclient_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:key="@string/key_ns_log_app_started_event" android:key="@string/key_ns_log_app_started_event"
android:title="@string/ns_log_app_started_event" android:title="@string/ns_log_app_started_event"
android:summary="@string/ns_log_app_started_event" android:summary="@string/ns_log_app_started_event" />
/>
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"

View file

@ -9,7 +9,7 @@
app:initialExpandedChildrenCount="0"> app:initialExpandedChildrenCount="0">
<info.nightscout.core.validators.ValidatingEditTextPreference <info.nightscout.core.validators.ValidatingEditTextPreference
android:defaultValue="https://{YOUR-SITE}.azurewebsites.net/" android:defaultValue="https://{YOUR-SITE}.azurewebsites.net"
android:dialogMessage="@string/ns_client_url_dialog_message" android:dialogMessage="@string/ns_client_url_dialog_message"
android:inputType="textUri" android:inputType="textUri"
android:key="@string/key_nsclientinternal_url" android:key="@string/key_nsclientinternal_url"
@ -26,6 +26,12 @@
validate:minLength="17" validate:minLength="17"
validate:testType="minLength" /> validate:testType="minLength" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_use_ws"
android:summary="@string/ns_use_ws_summary"
android:title="@string/ns_use_ws_title" />
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="@string/ns_sync_options" android:key="@string/ns_sync_options"
android:title="@string/ns_sync_options"> android:title="@string/ns_sync_options">
@ -178,14 +184,13 @@
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="nsclient_advanced" android:key="nsclient_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advanced_settings_title">
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:key="@string/key_ns_log_app_started_event" android:key="@string/key_ns_log_app_started_event"
android:title="@string/ns_log_app_started_event" android:title="@string/ns_log_app_started_event"
android:summary="@string/ns_log_app_started_event" android:summary="@string/ns_log_app_started_event" />
/>
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"

View file

@ -24,49 +24,57 @@
android:key="@string/key_tidepool_test_login" android:key="@string/key_tidepool_test_login"
android:title="@string/title_tidepool_test_login" /> android:title="@string/title_tidepool_test_login" />
<CheckBoxPreference <androidx.preference.PreferenceScreen
android:key="@string/connection_settings_title"
android:title="@string/connection_settings_title">
<SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:key="@string/key_tidepool_upload_cgm" android:key="@string/key_ns_cellular"
android:title="@string/tidepool_upload_cgm" /> android:title="@string/ns_cellular" />
<SwitchPreference
android:defaultValue="true"
android:dependency="@string/key_ns_cellular"
android:key="@string/key_ns_allow_roaming"
android:title="@string/ns_allow_roaming" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_wifi"
android:title="@string/ns_wifi" />
<EditTextPreference
android:dialogMessage="@string/ns_wifi_allowed_ssids"
android:dependency="@string/key_ns_wifi"
android:inputType="text"
android:key="@string/key_ns_wifi_ssids"
android:title="@string/ns_wifi_ssids" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_battery"
android:title="@string/ns_battery" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_charging"
android:title="@string/ns_charging" />
</androidx.preference.PreferenceScreen>
<androidx.preference.PreferenceScreen
android:key="nsclient_advanced"
android:title="@string/advanced_settings_title">
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="true" android:defaultValue="false"
android:key="@string/key_tidepool_upload_bolus"
android:title="@string/tidepool_upload_bolus" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_bg"
android:title="@string/tidepool_upload_bg" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_tbr"
android:title="@string/tidepool_upload_tbr" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_profile"
android:title="@string/tidepool_upload_profile" />
<CheckBoxPreference
android:defaultValue="true"
android:enabled="false"
android:key="@string/key_tidepool_dev_servers" android:key="@string/key_tidepool_dev_servers"
android:summary="@string/summary_tidepool_dev_servers" android:summary="@string/summary_tidepool_dev_servers"
android:title="@string/title_tidepool_dev_servers" /> android:title="@string/title_tidepool_dev_servers" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_tidepool_only_while_charging"
android:summary="Upload data only when charging"
android:title="Only when charging" />
<CheckBoxPreference </androidx.preference.PreferenceScreen>
android:defaultValue="false"
android:key="@string/key_tidepool_only_while_unmetered"
android:summary="Upload data only when connected to an unmetered network like Wifi"
android:title="Only on Wifi" />
</PreferenceCategory> </PreferenceCategory>

View file

@ -1,88 +0,0 @@
package info.nightscout.plugins.sync.nsclient
import info.nightscout.androidaps.TestBase
import info.nightscout.interfaces.receivers.ReceiverStatusStore
import info.nightscout.plugins.sync.R
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventChargingState
import info.nightscout.rx.events.EventNetworkChange
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import org.junit.Assert
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
class NsClientReceiverDelegateTest : TestBase() {
@Mock lateinit var sp: SP
@Mock lateinit var rh: ResourceHelper
val rxBus = RxBus(aapsSchedulers, aapsLogger)
@Mock private lateinit var receiverStatusStore: ReceiverStatusStore
private lateinit var sut: NsClientReceiverDelegate
@BeforeEach
fun prepare() {
//receiverStatusStore = ReceiverStatusStore(context, rxBus)
sut = NsClientReceiverDelegate(rxBus, rh, sp, receiverStatusStore)
}
@Test
fun testCalculateStatusChargingState() {
`when`(sp.getBoolean(R.string.key_ns_battery, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_charging, true)).thenReturn(false)
Assert.assertTrue(sut.calculateStatus(EventChargingState(false, 0)))
Assert.assertFalse(sut.calculateStatus(EventChargingState(true, 0)))
`when`(sp.getBoolean(R.string.key_ns_battery, true)).thenReturn(false)
`when`(sp.getBoolean(R.string.key_ns_charging, true)).thenReturn(true)
Assert.assertTrue(sut.calculateStatus(EventChargingState(true, 0)))
Assert.assertFalse(sut.calculateStatus(EventChargingState(false, 0)))
}
@Test
fun testCalculateStatusNetworkState() {
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = true)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = false)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = true, wifiConnected = true)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = false, wifiConnected = true)))
Assert.assertFalse(sut.calculateStatus(EventNetworkChange()))
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("test")
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = true)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = false)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = true, wifiConnected = true)))
Assert.assertFalse(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = false, wifiConnected = true)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "test", mobileConnected = true, wifiConnected = true)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "test", mobileConnected = false, wifiConnected = true)))
Assert.assertFalse(sut.calculateStatus(EventNetworkChange()))
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(false)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(true)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(wifiConnected = true)))
Assert.assertFalse(sut.calculateStatus(EventNetworkChange()))
Assert.assertFalse(sut.calculateStatus(EventNetworkChange(mobileConnected = true)))
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(false)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = false)))
Assert.assertFalse(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = true)))
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(true)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = false)))
Assert.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = true)))
}
}

View file

@ -0,0 +1,89 @@
package info.nightscout.plugins.sync.nsclient
import info.nightscout.androidaps.TestBase
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.receivers.ReceiverStatusStore
import info.nightscout.plugins.sync.R
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventChargingState
import info.nightscout.rx.events.EventNetworkChange
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
class ReceiverDelegateTest : TestBase() {
@Mock lateinit var sp: SP
@Mock lateinit var rh: ResourceHelper
@Mock lateinit var fabricPrivacy: FabricPrivacy
private val rxBus = RxBus(aapsSchedulers, aapsLogger)
@Mock private lateinit var receiverStatusStore: ReceiverStatusStore
private lateinit var sut: ReceiverDelegate
@BeforeEach
fun prepare() {
//receiverStatusStore = ReceiverStatusStore(context, rxBus)
sut = ReceiverDelegate(rxBus, rh, sp, receiverStatusStore, aapsSchedulers, fabricPrivacy)
}
@Test
fun testCalculateStatusChargingState() {
`when`(sp.getBoolean(R.string.key_ns_battery, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_charging, true)).thenReturn(false)
Assertions.assertTrue(sut.calculateStatus(EventChargingState(false, 0)))
Assertions.assertFalse(sut.calculateStatus(EventChargingState(true, 0)))
`when`(sp.getBoolean(R.string.key_ns_battery, true)).thenReturn(false)
`when`(sp.getBoolean(R.string.key_ns_charging, true)).thenReturn(true)
Assertions.assertTrue(sut.calculateStatus(EventChargingState(true, 0)))
Assertions.assertFalse(sut.calculateStatus(EventChargingState(false, 0)))
}
@Test
fun testCalculateStatusNetworkState() {
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = true)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = false)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = true, wifiConnected = true)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = false, wifiConnected = true)))
Assertions.assertFalse(sut.calculateStatus(EventNetworkChange()))
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("test")
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = true)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, wifiConnected = false, roaming = false)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = true, wifiConnected = true)))
Assertions.assertFalse(sut.calculateStatus(EventNetworkChange(ssid = "<unknown ssid>", mobileConnected = false, wifiConnected = true)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "test", mobileConnected = true, wifiConnected = true)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(ssid = "test", mobileConnected = false, wifiConnected = true)))
Assertions.assertFalse(sut.calculateStatus(EventNetworkChange()))
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(false)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(true)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(wifiConnected = true)))
Assertions.assertFalse(sut.calculateStatus(EventNetworkChange()))
Assertions.assertFalse(sut.calculateStatus(EventNetworkChange(mobileConnected = true)))
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(false)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = false)))
Assertions.assertFalse(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = true)))
`when`(sp.getBoolean(R.string.key_ns_cellular, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_wifi, true)).thenReturn(true)
`when`(sp.getBoolean(R.string.key_ns_allow_roaming, true)).thenReturn(true)
`when`(sp.getString(R.string.key_ns_wifi_ssids, "")).thenReturn("")
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = false)))
Assertions.assertTrue(sut.calculateStatus(EventNetworkChange(mobileConnected = true, roaming = true)))
}
}

View file

@ -1,9 +1,11 @@
package info.nightscout.plugins.sync.nsclientV3 package info.nightscout.plugins.sync.nsclientV3
import androidx.work.WorkManager
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.core.extensions.fromConstant import info.nightscout.core.extensions.fromConstant
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.database.entities.Bolus import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.BolusCalculatorResult import info.nightscout.database.entities.BolusCalculatorResult
import info.nightscout.database.entities.Carbs import info.nightscout.database.entities.Carbs
@ -26,8 +28,10 @@ import info.nightscout.interfaces.pump.VirtualPump
import info.nightscout.interfaces.source.NSClientSource import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclient.NsClientReceiverDelegate import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclient.extensions.fromConstant import info.nightscout.plugins.sync.nsclient.extensions.fromConstant
import info.nightscout.sdk.interfaces.NSAndroidClient import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse
@ -45,7 +49,7 @@ import org.mockito.internal.verification.Times
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
internal class NSClientV3PluginTest : TestBaseWithProfile() { internal class NSClientV3PluginTest : TestBaseWithProfile() {
@Mock lateinit var nsClientReceiverDelegate: NsClientReceiverDelegate @Mock lateinit var receiverDelegate: ReceiverDelegate
@Mock lateinit var uiInteraction: UiInteraction @Mock lateinit var uiInteraction: UiInteraction
@Mock lateinit var dataSyncSelector: DataSyncSelector @Mock lateinit var dataSyncSelector: DataSyncSelector
@Mock lateinit var nsAndroidClient: NSAndroidClient @Mock lateinit var nsAndroidClient: NSAndroidClient
@ -54,6 +58,10 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
@Mock lateinit var xDripBroadcast: XDripBroadcast @Mock lateinit var xDripBroadcast: XDripBroadcast
@Mock lateinit var virtualPump: VirtualPump @Mock lateinit var virtualPump: VirtualPump
@Mock lateinit var mockedProfileFunction: ProfileFunction @Mock lateinit var mockedProfileFunction: ProfileFunction
@Mock lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler
@Mock lateinit var workManager: WorkManager
@Mock lateinit var workerClasses: WorkerClasses
@Mock lateinit var dataWorkerStorage: DataWorkerStorage
private lateinit var storeDataForDb: StoreDataForDb private lateinit var storeDataForDb: StoreDataForDb
private lateinit var sut: NSClientV3Plugin private lateinit var sut: NSClientV3Plugin
@ -65,11 +73,12 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
@BeforeEach @BeforeEach
fun prepare() { fun prepare() {
storeDataForDb = StoreDataForDbImpl(aapsLogger, rxBus, repository, sp, uel, dateUtil, config, nsClientSource, xDripBroadcast, virtualPump, uiInteraction) storeDataForDb = StoreDataForDbImpl(aapsLogger, rxBus, repository, sp, uel, dateUtil, config, nsClientSource, virtualPump, uiInteraction)
sut = sut =
NSClientV3Plugin( NSClientV3Plugin(
injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy, injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy,
sp, nsClientReceiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, mockedProfileFunction, repository sp, receiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, mockedProfileFunction, repository,
nsDeviceStatusHandler, workManager, workerClasses, dataWorkerStorage, nsClientSource
) )
sut.nsAndroidClient = nsAndroidClient sut.nsAndroidClient = nsAndroidClient
`when`(mockedProfileFunction.getProfile(anyLong())).thenReturn(validProfile) `when`(mockedProfileFunction.getProfile(anyLong())).thenReturn(validProfile)

View file

@ -9,6 +9,7 @@ import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -27,6 +28,7 @@ internal class DataSyncWorkerTest : TestBase() {
@Mock lateinit var activePlugin: ActivePlugin @Mock lateinit var activePlugin: ActivePlugin
@Mock lateinit var nsClient: NsClient @Mock lateinit var nsClient: NsClient
@Mock lateinit var rxBus: RxBus @Mock lateinit var rxBus: RxBus
@Mock lateinit var nsClientV3Plugin: NSClientV3Plugin
private lateinit var sut: DataSyncWorker private lateinit var sut: DataSyncWorker
@ -38,6 +40,7 @@ internal class DataSyncWorkerTest : TestBase() {
it.dataSyncSelector = dataSyncSelector it.dataSyncSelector = dataSyncSelector
it.activePlugin = activePlugin it.activePlugin = activePlugin
it.rxBus = rxBus it.rxBus = rxBus
it.nsClientV3Plugin = nsClientV3Plugin
} }
} }
} }

View file

@ -22,7 +22,8 @@ import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.workflow.WorkerClasses import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.nsclient.NsClientReceiverDelegate import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSSvgV3 import info.nightscout.plugins.sync.nsclientV3.extensions.toNSSvgV3
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
@ -37,6 +38,7 @@ import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq import org.mockito.ArgumentMatchers.eq
@ -49,7 +51,6 @@ internal class LoadBgWorkerTest : TestBase() {
@Mock lateinit var workerClasses: WorkerClasses @Mock lateinit var workerClasses: WorkerClasses
@Mock lateinit var sp: SP @Mock lateinit var sp: SP
@Mock lateinit var fabricPrivacy: FabricPrivacy @Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var rxBus: RxBus
@Mock lateinit var dateUtil: DateUtil @Mock lateinit var dateUtil: DateUtil
@Mock lateinit var nsAndroidClient: NSAndroidClient @Mock lateinit var nsAndroidClient: NSAndroidClient
@Mock lateinit var rh: ResourceHelper @Mock lateinit var rh: ResourceHelper
@ -62,9 +63,11 @@ internal class LoadBgWorkerTest : TestBase() {
@Mock lateinit var nsClientSource: NSClientSource @Mock lateinit var nsClientSource: NSClientSource
@Mock lateinit var workManager: WorkManager @Mock lateinit var workManager: WorkManager
@Mock lateinit var workContinuation: WorkContinuation @Mock lateinit var workContinuation: WorkContinuation
@Mock lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler
private val rxBus: RxBus = RxBus(aapsSchedulers, aapsLogger)
private lateinit var nsClientV3Plugin: NSClientV3Plugin private lateinit var nsClientV3Plugin: NSClientV3Plugin
private lateinit var nsClientReceiverDelegate: NsClientReceiverDelegate private lateinit var receiverDelegate: ReceiverDelegate
private lateinit var dataWorkerStorage: DataWorkerStorage private lateinit var dataWorkerStorage: DataWorkerStorage
private lateinit var sut: LoadBgWorker private lateinit var sut: LoadBgWorker
@ -95,10 +98,11 @@ internal class LoadBgWorkerTest : TestBase() {
Mockito.`when`(dateUtil.now()).thenReturn(now) Mockito.`when`(dateUtil.now()).thenReturn(now)
Mockito.`when`(nsClientSource.isEnabled()).thenReturn(true) Mockito.`when`(nsClientSource.isEnabled()).thenReturn(true)
dataWorkerStorage = DataWorkerStorage(context) dataWorkerStorage = DataWorkerStorage(context)
nsClientReceiverDelegate = NsClientReceiverDelegate(rxBus, rh, sp, receiverStatusStore) receiverDelegate = ReceiverDelegate(rxBus, rh, sp, receiverStatusStore, aapsSchedulers, fabricPrivacy)
nsClientV3Plugin = NSClientV3Plugin( nsClientV3Plugin = NSClientV3Plugin(
injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy, sp, nsClientReceiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy,
profileFunction, repository sp, receiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, profileFunction, repository,
nsDeviceStatusHandler, workManager, workerClasses, dataWorkerStorage, nsClientSource
) )
nsClientV3Plugin.newestDataOnServer = LastModified(LastModified.Collections()) nsClientV3Plugin.newestDataOnServer = LastModified(LastModified.Collections())
} }
@ -121,7 +125,7 @@ internal class LoadBgWorkerTest : TestBase() {
Assertions.assertTrue(result is ListenableWorker.Result.Success) Assertions.assertTrue(result is ListenableWorker.Result.Success)
Assertions.assertTrue(result.outputData.getString("Result") == "Load not enabled") Assertions.assertTrue(result.outputData.getString("Result") == "Load not enabled")
Mockito.verify(workManager, Mockito.times(1)).enqueueUniqueWork( Mockito.verify(workManager, Mockito.times(1)).enqueueUniqueWork(
eq(NSClientV3Plugin.JOB_NAME), eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE), eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>() any<OneTimeWorkRequest>()
) )
@ -135,13 +139,13 @@ internal class LoadBgWorkerTest : TestBase() {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = 0L // first load nsClientV3Plugin.lastLoadedSrvModified.collections.entries = 0L // first load
nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000 nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000
sut = TestListenableWorkerBuilder<LoadBgWorker>(context).build() sut = TestListenableWorkerBuilder<LoadBgWorker>(context).build()
Mockito.`when`(nsAndroidClient.getSgvsNewerThan(anyLong(), anyLong())).thenReturn(NSAndroidClient.ReadResponse(200, 0, emptyList())) Mockito.`when`(nsAndroidClient.getSgvsNewerThan(anyLong(), anyInt())).thenReturn(NSAndroidClient.ReadResponse(200, 0, emptyList()))
val result = sut.doWorkAndLog() val result = sut.doWorkAndLog()
Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries) Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries)
Assertions.assertTrue(result is ListenableWorker.Result.Success) Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork( Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(NSClientV3Plugin.JOB_NAME), eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE), eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>() any<OneTimeWorkRequest>()
) )
@ -172,12 +176,12 @@ internal class LoadBgWorkerTest : TestBase() {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = 0L // first load nsClientV3Plugin.lastLoadedSrvModified.collections.entries = 0L // first load
nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000 nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000
sut = TestListenableWorkerBuilder<LoadBgWorker>(context).build() sut = TestListenableWorkerBuilder<LoadBgWorker>(context).build()
Mockito.`when`(nsAndroidClient.getSgvsNewerThan(anyLong(), anyLong())).thenReturn(NSAndroidClient.ReadResponse(200, 0, listOf(glucoseValue.toNSSvgV3()))) Mockito.`when`(nsAndroidClient.getSgvsNewerThan(anyLong(), anyInt())).thenReturn(NSAndroidClient.ReadResponse(200, 0, listOf(glucoseValue.toNSSvgV3())))
val result = sut.doWorkAndLog() val result = sut.doWorkAndLog()
Assertions.assertTrue(result is ListenableWorker.Result.Success) Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork( Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(NSClientV3Plugin.JOB_NAME), eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE), eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>() any<OneTimeWorkRequest>()
) )
@ -194,13 +198,13 @@ internal class LoadBgWorkerTest : TestBase() {
nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000 nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000
nsClientV3Plugin.newestDataOnServer?.collections?.entries = now - 2000 nsClientV3Plugin.newestDataOnServer?.collections?.entries = now - 2000
sut = TestListenableWorkerBuilder<LoadBgWorker>(context).build() sut = TestListenableWorkerBuilder<LoadBgWorker>(context).build()
Mockito.`when`(nsAndroidClient.getSgvsNewerThan(anyLong(), anyLong())).thenReturn(NSAndroidClient.ReadResponse(200, 0, emptyList())) Mockito.`when`(nsAndroidClient.getSgvsNewerThan(anyLong(), anyInt())).thenReturn(NSAndroidClient.ReadResponse(200, 0, emptyList()))
val result = sut.doWorkAndLog() val result = sut.doWorkAndLog()
Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries) Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries)
Assertions.assertTrue(result is ListenableWorker.Result.Success) Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork( Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(NSClientV3Plugin.JOB_NAME), eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE), eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>() any<OneTimeWorkRequest>()
) )