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.NotNSClient notNsClient: 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> {
val plugins = allConfigs.toMutableMap()
if (config.PUMPDRIVERS) plugins += pumpDrivers.get()
if (config.APS) plugins += aps.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 }
}

View file

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

View file

@ -15,7 +15,7 @@ class ConfigImpl @Inject constructor(
fileListProvider: PrefFileListProvider
) : 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 NSCLIENT = BuildConfig.FLAVOR == "aapsclient" || BuildConfig.FLAVOR == "aapsclient2"
override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol"

View file

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

View file

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

View file

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

View file

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

View file

@ -22,4 +22,15 @@ data class LastModified(
@SerializedName("foods") var foods: Long = 0, // foods 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="mute">Mute</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="aps_mode_title">APS Mode</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_nsclientinternal_url" translatable="false">nsclientinternal_url</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_carbs" translatable="false">ns_receive_carbs</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.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.database.entities.interfaces.TraceableDBEntry
import info.nightscout.database.entities.interfaces.end
import java.util.TimeZone
@Entity(
@ -53,9 +54,6 @@ data class OfflineEvent(
previous.interfaceIDs.nightscoutId == null &&
interfaceIDs.nightscoutId != null
fun isRecordDeleted(other: OfflineEvent): Boolean =
isValid && !other.isValid
enum class Reason {
DISCONNECT_PUMP,
SUSPEND,

View file

@ -52,7 +52,7 @@
<androidx.preference.PreferenceScreen
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">
<intent

View file

@ -162,7 +162,7 @@
<androidx.preference.PreferenceScreen
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">
<intent

View file

@ -183,7 +183,7 @@
<androidx.preference.PreferenceScreen
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">
<intent

View file

@ -75,7 +75,7 @@ class RunningConfigurationImpl @Inject constructor(
assert(config.NSCLIENT)
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())
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.nsclient.NSAlarm
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.plugins.R
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
@ -21,7 +20,6 @@ class NotificationWithAction constructor(
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var sp: SP
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var activePlugin: ActivePlugin
init {
@ -66,7 +64,7 @@ class NotificationWithAction constructor(
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
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
android:key="overview_advanced"
android:title="@string/advancedsettings_title">
android:title="@string/advanced_settings_title">
<SwitchPreference
android:defaultValue="false"

View file

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

View file

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

View file

@ -32,10 +32,7 @@ dependencies {
testImplementation project(':plugins:aps')
// NSClient, Tidepool
api("io.socket:socket.io-client:1.0.2") {
// excluding org.json which is provided by Android
exclude group: "org.json", module: "json"
}
api("io.socket:socket.io-client:2.1.0")
api "com.squareup.okhttp3:okhttp:$okhttp3_version"
api "com.squareup.okhttp3:logging-interceptor:$okhttp3_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 contributesStoreBgWorker(): StoreDataForDbImpl.StoreBgWorker
@ContributesAndroidInjector abstract fun contributesStoreFoodWorker(): StoreDataForDbImpl.StoreFoodWorker
@ContributesAndroidInjector abstract fun contributesStoreTreatmentsWorker(): StoreDataForDbImpl.StoreTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesTreatmentWorker(): LoadTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesProcessTreatmentsWorker(): ProcessTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesLoadDeviceStatusWorker(): LoadDeviceStatusWorker

View file

@ -57,7 +57,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
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()

View file

@ -81,7 +81,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
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 ->
uel.log(if (isChecked) UserEntry.Action.NS_PAUSED else UserEntry.Action.NS_RESUME, UserEntry.Sources.NSClient)
nsClientPlugin?.pause(isChecked)
@ -143,7 +143,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
private fun updateGui() {
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()
if (sp.getBoolean(R.string.key_ns_client_autoscroll, true)) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN)
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) =
if (containsKey(key)) merge(key, 1, Long::plus)
else put(key, 1)
override fun storeGlucoseValuesToDb() {
rxBus.send(EventNSClientNewLog("PROCESSING BG", ""))
if (glucoseValues.isNotEmpty())
repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
.doOnError {
@ -179,12 +190,10 @@ class StoreDataForDbImpl @Inject constructor(
sendLog("GlucoseValue", GlucoseValue::class.java.simpleName)
SystemClock.sleep(pause)
rxBus.send(EventNSClientNewLog("DONE BG", ""))
rxBus.send(EventNSClientNewLog("DONE PROCESSING BG", ""))
}
override fun storeFoodsToDb() {
rxBus.send(EventNSClientNewLog("PROCESSING FOOD", ""))
if (foods.isNotEmpty())
repository.runTransactionForResult(SyncNsFoodTransaction(foods))
.doOnError {
@ -209,12 +218,10 @@ class StoreDataForDbImpl @Inject constructor(
sendLog("Food", Food::class.java.simpleName)
SystemClock.sleep(pause)
rxBus.send(EventNSClientNewLog("DONE FOOD", ""))
rxBus.send(EventNSClientNewLog("DONE PROCESSING FOOD", ""))
}
override fun storeTreatmentsToDb() {
rxBus.send(EventNSClientNewLog("PROCESSING TR", ""))
if (boluses.isNotEmpty())
repository.runTransactionForResult(SyncNsBolusTransaction(boluses))
.doOnError {
@ -791,7 +798,7 @@ class StoreDataForDbImpl @Inject constructor(
SystemClock.sleep(pause)
uel.log(userEntries)
rxBus.send(EventNSClientNewLog("DONE TR", ""))
rxBus.send(EventNSClientNewLog("DONE PROCESSING TR", ""))
}
private val eventWorker = Executors.newSingleThreadScheduledExecutor()
@ -991,7 +998,7 @@ class StoreDataForDbImpl @Inject constructor(
sendLog("TherapyEvent", TherapyEvent::class.java.simpleName)
sendLog("OfflineEvent", OfflineEvent::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) {

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

View file

@ -1,43 +1,67 @@
package info.nightscout.plugins.sync.nsclient
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.receivers.ReceiverStatusStore
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.events.EventChargingState
import info.nightscout.rx.events.EventNetworkChange
import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.shared.interfaces.ResourceHelper
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.Singleton
@OpenForTesting
@Singleton
class NsClientReceiverDelegate @Inject constructor(
class ReceiverDelegate @Inject constructor(
private val rxBus: RxBus,
private val rh: ResourceHelper,
private val sp: SP,
private val receiverStatusStore: ReceiverStatusStore
private val receiverStatusStore: ReceiverStatusStore,
aapsSchedulers: AapsSchedulers,
fabricPrivacy: FabricPrivacy
) {
private var allowedChargingState = true
private var allowedNetworkState = true
var allowed = true
var blockingReason = ""
private var allowedChargingState: Boolean? = null
private var allowedNetworkState: Boolean? = null
var allowed: Boolean = false
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() {
receiverStatusStore.updateNetworkStatus()
}
fun onStatusEvent(ev: EventPreferenceChange) {
private fun onPreferenceChange(ev: EventPreferenceChange) {
when {
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_wifi_ssids)) ||
ev.isChanged(rh.gs(R.string.key_ns_allow_roaming)) -> {
receiverStatusStore.updateNetworkStatus()
receiverStatusStore.lastNetworkEvent?.let { onStatusEvent(it) }
receiverStatusStore.lastNetworkEvent?.let { onNetworkChange(it) }
}
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)
if (newChargingState != allowedChargingState) {
allowedChargingState = newChargingState
blockingReason = rh.gs(R.string.blocked_by_charging)
if (!newChargingState) blockingReason = rh.gs(R.string.blocked_by_charging)
processStateChange()
}
}
fun onStatusEvent(ev: EventNetworkChange) {
private fun onNetworkChange(ev: EventNetworkChange) {
val newNetworkState = calculateStatus(ev)
if (newNetworkState != allowedNetworkState) {
allowedNetworkState = newNetworkState
blockingReason = rh.gs(R.string.blocked_by_connectivity)
if (!newNetworkState) blockingReason = rh.gs(R.string.blocked_by_connectivity)
processStateChange()
}
}
private fun processStateChange() {
val newAllowedState = allowedChargingState && allowedNetworkState
val newAllowedState = allowedChargingState == true && allowedNetworkState == true
if (newAllowedState != allowed) {
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
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.configBuilder.RunningConfiguration
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
@ -65,6 +66,7 @@ import javax.inject.Singleton
*/
@Suppress("SpellCheckingInspection")
@Singleton
@OpenForTesting
class NSDeviceStatusHandler @Inject constructor(
private val sp: SP,
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.plugins.sync.R
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.EventNSClientUpdateGUI
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.EventNSClientNewLog
import info.nightscout.rx.events.EventNSClientRestart
import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
@ -139,13 +141,21 @@ class NSClientService : DaggerService() {
.subscribe({ event: EventPreferenceChange ->
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(R.string.key_ns_client_paused))
event.isChanged(rh.gs(R.string.key_ns_paused))
) {
latestDateInReceivedData = 0
destroy()
initialize()
}
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventConnectivityOptionChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
latestDateInReceivedData = 0
destroy()
initialize()
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io)
@ -165,6 +175,10 @@ class NSClientService : DaggerService() {
.toObservable(NSAuthAck::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ack -> processAuthAck(ack) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ resend("NEW_DATA") }, fabricPrivacy::logException)
}
override fun onDestroy() {
@ -213,10 +227,10 @@ class NSClientService : DaggerService() {
@Suppress("DEPRECATION")
if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString()
rxBus.send(EventNSClientStatus("Initializing"))
if (!nsClientPlugin.isAllowed) {
if (nsClientPlugin.isAllowed != true) {
rxBus.send(EventNSClientNewLog("NSCLIENT", 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(EventNSClientStatus("Paused"))
} else if (!nsEnabled) {
@ -225,16 +239,10 @@ class NSClientService : DaggerService() {
} else if (nsURL != "" && (nsURL.lowercase(Locale.getDefault()).startsWith("https://"))) {
try {
rxBus.send(EventNSClientStatus("Connecting ..."))
val opt = IO.Options()
opt.forceNew = true
opt.reconnection = true
val opt = IO.Options().also { it.forceNew = true }
socket = IO.socket(nsURL, opt).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnect)
socket.on(Socket.EVENT_DISCONNECT, onDisconnect)
socket.on(Socket.EVENT_ERROR, onError)
socket.on(Socket.EVENT_CONNECT_ERROR, onError)
socket.on(Socket.EVENT_CONNECT_TIMEOUT, onError)
socket.on(Socket.EVENT_PING, onPing)
rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect"))
socket.connect()
socket.on("dataUpdate", onDataUpdate)
@ -300,7 +308,6 @@ class NSClientService : DaggerService() {
@Synchronized fun destroy() {
socket?.off(Socket.EVENT_CONNECT)
socket?.off(Socket.EVENT_DISCONNECT)
socket?.off(Socket.EVENT_PING)
socket?.off("dataUpdate")
socket?.off("announcement")
socket?.off("alarm")
@ -336,18 +343,6 @@ class NSClientService : DaggerService() {
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 ->
/*
@ -651,7 +646,7 @@ class NSClientService : DaggerService() {
private fun handleAlarm(alarm: JSONObject) {
val defaultVal = config.NSCLIENT
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) {
val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm)
@ -664,7 +659,7 @@ class NSClientService : DaggerService() {
private fun handleUrgentAlarm(alarm: JSONObject) {
val defaultVal = config.NSCLIENT
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) {
val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm)

View file

@ -3,6 +3,7 @@ package info.nightscout.plugins.sync.nsclientV3
import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import android.os.SystemClock
import android.text.Spanned
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
@ -16,26 +17,33 @@ import com.google.gson.GsonBuilder
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.interfaces.TraceableDBEntry
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HtmlHelper
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.R
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.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.toNSBolusWizard
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.toNSTemporaryTarget
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.LoadLastModificationWorker
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.bus.RxBus
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.EventNetworkChange
import info.nightscout.rx.events.EventNewBG
import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPreferenceChange
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.sdk.NSAndroidClientImpl
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.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
@ -73,15 +86,19 @@ import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.disposables.CompositeDisposable
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.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import org.json.JSONArray
import org.json.JSONObject
import java.net.URISyntaxException
import javax.inject.Inject
import javax.inject.Singleton
@ -96,13 +113,18 @@ class NSClientV3Plugin @Inject constructor(
private val context: Context,
private val fabricPrivacy: FabricPrivacy,
private val sp: SP,
private val nsClientReceiverDelegate: NsClientReceiverDelegate,
private val receiverDelegate: ReceiverDelegate,
private val config: Config,
private val dateUtil: DateUtil,
private val uiInteraction: UiInteraction,
private val dataSyncSelector: DataSyncSelector,
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(
PluginDescription()
.mainType(PluginType.SYNC)
@ -115,13 +137,14 @@ class NSClientV3Plugin @Inject constructor(
aapsLogger, rh, injector
) {
@Suppress("PropertyName")
val JOB_NAME: String = this::class.java.simpleName
companion object {
val REFRESH_INTERVAL = T.secs(30).msecs()
const val RECORDS_TO_LOAD = 500
}
@Suppress("PropertyName") val JOB_NAME: String = this::class.java.simpleName
private val disposable = CompositeDisposable()
var scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private lateinit var runLoop: Runnable
@ -130,21 +153,23 @@ class NSClientV3Plugin @Inject constructor(
override val status
get() =
when {
sp.getBoolean(R.string.key_ns_client_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused)
isAllowed.not() -> blockingReason
lastOperationError != null -> rh.gs(info.nightscout.core.ui.R.string.error)
nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected)
workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working)
nsAndroidClient?.lastStatus?.apiPermissions?.isFull() == true -> rh.gs(info.nightscout.shared.R.string.connected)
nsAndroidClient?.lastStatus?.apiPermissions?.isRead() == true -> rh.gs(R.string.read_only)
else -> rh.gs(info.nightscout.core.ui.R.string.unknown)
sp.getBoolean(R.string.key_ns_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused)
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)
nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected)
workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working)
nsAndroidClient?.lastStatus?.apiPermissions?.isFull() == true -> rh.gs(info.nightscout.shared.R.string.connected)
nsAndroidClient?.lastStatus?.apiPermissions?.isRead() == true -> rh.gs(R.string.read_only)
else -> rh.gs(info.nightscout.core.ui.R.string.unknown)
}
var lastOperationError: String? = null
internal var nsAndroidClient: NSAndroidClient? = null
private val isAllowed get() = nsClientReceiverDelegate.allowed
private val blockingReason get() = nsClientReceiverDelegate.blockingReason
private val isAllowed get() = receiverDelegate.allowed
private val blockingReason get() = receiverDelegate.blockingReason
val maxAge = T.days(77).msecs()
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
.toObservable(EventNetworkChange::class.java)
.toObservable(EventConnectivityOptionChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev)
setClient()
rxBus.send(EventNSClientUpdateGUI())
rxBus.send(EventNSClientNewLog("● CONNECTIVITY", ev.blockingReason))
setClient("CONNECTIVITY")
if (isAllowed) executeLoop("CONNECTIVITY", forceNew = false)
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev)
if (ev.isChanged(rh.gs(R.string.key_ns_client_token)) || ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)))
setClient()
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_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)
disposable += rxBus
.toObservable(EventAppExit::class.java)
@ -191,46 +224,56 @@ class NSClientV3Plugin @Inject constructor(
addToLog(event)
aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText)
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventChargingState::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev)
rxBus.send(EventNSClientUpdateGUI())
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNSClientResend::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ delayAndScheduleExecution("NEW_BG") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ delayAndScheduleExecution("NEW_DATA") }, fabricPrivacy::logException)
.subscribe({ executeUpload("NEW_DATA", forceNew = false) }, fabricPrivacy::logException)
runLoop = Runnable {
handler.postDelayed(runLoop, REFRESH_INTERVAL)
var refreshInterval = T.mins(5).msecs()
repository.getLastGlucoseValueWrapped().blockingGet().let {
// if last value is older than 5 min or there is no bg
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)
else {
if (isAllowed) rxBus.send(EventNSClientNewLog("RECENT", "No need to load"))
}
} 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() {
handler.removeCallbacksAndMessages(null)
disposable.clear()
storageSocket?.disconnect()
alarmSocket?.disconnect()
super.onStop()
}
@ -254,7 +297,7 @@ class NSClientV3Plugin @Inject constructor(
}
}
private fun setClient() {
private fun setClient(reason: String) {
nsAndroidClient = NSAndroidClientImpl(
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, ""),
@ -262,9 +305,266 @@ class NSClientV3Plugin @Inject constructor(
logging = true,
logger = { msg -> aapsLogger.debug(LTag.HTTP, msg) }
)
if (wsConnected) {
storageSocket?.disconnect()
alarmSocket?.disconnect()
}
SystemClock.sleep(2000)
initializeWebSockets(reason)
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) {
synchronized(listLog) {
listLog.add(ev)
@ -290,32 +590,21 @@ class NSClientV3Plugin @Inject constructor(
}
override fun resend(reason: String) {
executeLoop("RESEND", forceNew = false)
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true))
executeUpload("RESEND", forceNew = false)
else
executeLoop("RESEND", forceNew = false)
}
override fun pause(newState: Boolean) {
sp.putBoolean(R.string.key_ns_client_paused, newState)
rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_client_paused)))
sp.putBoolean(R.string.key_ns_paused, newState)
rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_paused)))
}
override fun detectedNsVersion(): String? = nsAndroidClient?.lastStatus?.version
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) =
when (collection) {
NsClient.Collection.ENTRIES -> lastLoadedSrvModified.collections.entries == 0L
@ -335,6 +624,7 @@ class NSClientV3Plugin @Inject constructor(
override fun resetToFullSync() {
firstLoadContinueTimestamp = LastModified(LastModified.Collections())
lastLoadedSrvModified = LastModified(LastModified.Collections())
initialLoadFinished = false
storeLastLoadedSrvModified()
dataSyncSelector.resetToNextFullSync()
}
@ -354,15 +644,15 @@ class NSClientV3Plugin @Inject constructor(
val data = (dataPair as DataSyncSelector.PairProfileStore).value
scope.launch {
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 ->
when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ProfileStore"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ProfileStore"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ProfileStore"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ProfileStore"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> {
rxBus.send(EventNSClientNewLog("ERROR", "ProfileStore"))
rxBus.send(EventNSClientNewLog("ERROR", "ProfileStore"))
return@launch
}
}
@ -384,15 +674,15 @@ class NSClientV3Plugin @Inject constructor(
val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toNSDeviceStatus()
scope.launch {
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 ->
when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName} ${result.identifier}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName} ${result.identifier}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch
}
}
@ -425,24 +715,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send(
EventNSClientNewLog(
when (operation) {
Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection"
Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection"
},
when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress"
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
}
)
)
call?.let { it(data) }?.let { result ->
when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "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}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "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}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch
}
}
@ -480,24 +770,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send(
EventNSClientNewLog(
when (operation) {
Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection"
Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection"
},
when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress"
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
}
)
)
call?.let { it(data) }?.let { result ->
when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "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}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "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}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch
}
}
@ -563,24 +853,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send(
EventNSClientNewLog(
when (operation) {
Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection"
Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection"
},
when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress"
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
}
)
)
call?.let { it(data) }?.let { result ->
when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "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}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "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}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
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))
}
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) {
if (sp.getBoolean(R.string.key_ns_client_paused, false)) {
rxBus.send(EventNSClientNewLog("RUN", "paused"))
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && initialLoadFinished) return
if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("● RUN", "paused $origin"))
return
}
if (!isAllowed) {
rxBus.send(EventNSClientNewLog("RUN", blockingReason))
rxBus.send(EventNSClientNewLog("● RUN", "$blockingReason $origin"))
return
}
if (workIsRunning(arrayOf(JOB_NAME))) {
rxBus.send(EventNSClientNewLog("RUN", "Already running $origin"))
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 next round $origin"))
rxBus.send(EventNSClientNewLog("RUN", "Starting next round $origin"))
WorkManager.getInstance(context)
.beginUniqueWork(
JOB_NAME,
@ -753,6 +1031,30 @@ class NSClientV3Plugin @Inject constructor(
.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 {
for (workName in workNames)
for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get())
@ -760,20 +1062,4 @@ class NSClientV3Plugin @Inject constructor(
return true
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.sync.DataSyncSelector
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.events.EventNSClientNewLog
import kotlinx.coroutines.Dispatchers
@ -20,12 +21,17 @@ class DataSyncWorker(
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var rxBus: RxBus
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
override suspend fun doWorkAndLog(): Result {
if (activePlugin.activeNsClient?.hasWritePermission == true) {
rxBus.send(EventNSClientNewLog("UPL", "Start"))
if (activePlugin.activeNsClient?.hasWritePermission == true || nsClientV3Plugin.wsConnected) {
rxBus.send(EventNSClientNewLog("UPL", "Start"))
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())
return Result.success()

View file

@ -71,7 +71,7 @@ class LoadBgWorker(
aapsLogger.debug(LTag.NSCLIENT, "SGVS: $sgvs")
if (sgvs.isNotEmpty()) {
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
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
@ -92,7 +92,7 @@ class LoadBgWorker(
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
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
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
@ -108,7 +108,7 @@ class LoadBgWorker(
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
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
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
@ -120,7 +120,7 @@ class LoadBgWorker(
}
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = 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"))
try {
// Notify plugin we loaded al missed data
nsClientV3Plugin.initialLoadFinished = true
val from = dateUtil.now() - T.mins(7).msecs()
val deviceStatuses = nsAndroidClient.getDeviceStatusModifiedSince(from)
aapsLogger.debug("DEVICESTATUSES: $deviceStatuses")
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())
rxBus.send(EventNSClientNewLog("DONE DS", ""))
rxBus.send(EventNSClientNewLog("DONE PROCESSING DS", ""))
} 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)
.enqueueUniqueWork(
@ -51,7 +54,7 @@ class LoadDeviceStatusWorker(
)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = 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) {
val foods: List<NSFood> = nsAndroidClient.getFoods(1000).values
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
WorkManager.getInstance(context)
.beginUniqueWork(
@ -53,7 +53,7 @@ class LoadFoodsWorker(
.then(OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build())
.enqueue()
} else {
rxBus.send(EventNSClientNewLog("RCV", "FOOD skipped"))
rxBus.send(EventNSClientNewLog("RCV FOOD", "skipped"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
@ -63,7 +63,7 @@ class LoadFoodsWorker(
}
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = 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}")
} catch (error: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = 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.storeLastLoadedSrvModified()
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)
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
@ -69,7 +69,7 @@ class LoadProfileStoreWorker(
).then(OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build())
.enqueue()
} 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)
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
@ -78,7 +78,7 @@ class LoadProfileStoreWorker(
)
}
} 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)
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
@ -88,7 +88,7 @@ class LoadProfileStoreWorker(
}
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
return Result.failure(workDataOf("Error" to error.localizedMessage))
}

View file

@ -26,7 +26,7 @@ class LoadStatusWorker(
aapsLogger.debug(LTag.NSCLIENT, "STATUS: $status")
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = 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.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
@ -58,7 +59,7 @@ class LoadTreatmentsWorker(
aapsLogger.debug(LTag.NSCLIENT, "TREATMENTS: $treatments")
if (treatments.isNotEmpty()) {
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
val stopLoading = treatments.size != NSClientV3Plugin.RECORDS_TO_LOAD || response.code == 304
WorkManager.getInstance(context)
@ -66,7 +67,7 @@ class LoadTreatmentsWorker(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(response))
.setInputData(dataWorkerStorage.storeInputData(response.values))
.build()
)
// 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.storeLastLoadedSrvModified()
}
rxBus.send(EventNSClientNewLog("RCV END", "No TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb()
rxBus.send(EventNSClientNewLog("◄ RCV TR END", "No data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
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 {
// End first load
@ -94,18 +96,19 @@ class LoadTreatmentsWorker(
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded
nsClientV3Plugin.storeLastLoadedSrvModified()
}
rxBus.send(EventNSClientNewLog("RCV END", "No new TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb()
rxBus.send(EventNSClientNewLog("◄ RCV TR END", "No new data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
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) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage
return Result.failure(workDataOf("Error" to error.localizedMessage))
}

View file

@ -72,7 +72,7 @@ class ProcessFoodWorker(
storeDataForDb.foods.addAll(foods)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage))
rxBus.send(EventNSClientNewLog("ERROR", 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.events.EventNSClientNewLog
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.NSBolusWizard
import info.nightscout.sdk.localmodel.treatment.NSCarbs
@ -57,12 +56,12 @@ class ProcessTreatmentsWorker(
override suspend fun doWorkAndLog(): Result {
@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"))
try {
var latestDateInReceivedData: Long = 0
for (treatment in treatments.values) {
for (treatment in treatments) {
aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment")
val date = treatment.date ?: continue
if (date > latestDateInReceivedData) latestDateInReceivedData = date
@ -139,7 +138,7 @@ class ProcessTreatmentsWorker(
// xDripBroadcast.sendTreatments(treatments)
} catch (error: Exception) {
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.success()

View file

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

View file

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

View file

@ -8,7 +8,7 @@ import okhttp3.Response
import okio.Buffer
import java.io.IOException
class InfoInterceptor(val tag: String = "interceptor", val aapsLogger: AAPSLogger) : Interceptor {
class InfoInterceptor(val aapsLogger: AAPSLogger) : Interceptor {
@Throws(IOException::class)
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 okhttp3.Headers
class Session(val authHeader: String?,
private val sessionTokenHeader: String,
val service: TidepoolApiService?) {
class Session(
val authHeader: String?,
private val sessionTokenHeader: String,
val service: TidepoolApiService?
) {
internal var token: String? = null
internal var authReply: AuthReplyMessage? = null
internal var datasetReply: DatasetReplyMessage? = null
internal var start: Long = 0
internal var end: Long = 0
@Volatile
internal var iterations: Int = 0
fun populateHeaders(headers: Headers) {
if (this.token == null) {
this.token = headers.get(sessionTokenHeader)
this.token = headers[sessionTokenHeader]
}
}
fun populateBody(obj: Any?) {
if (obj == null) return
if (obj is AuthReplyMessage) {
authReply = obj
} else if (obj is List<*>) {
val list = obj as? List<*>?
when (obj) {
is AuthReplyMessage -> authReply = obj
list?.getOrNull(0)?.let {
if (it is DatasetReplyMessage) {
datasetReply = it
is List<*> ->
(obj as? List<*>?)?.getOrNull(0)?.let {
if (it is DatasetReplyMessage) datasetReply = it
}
}
} else if (obj is DatasetReplyMessage) {
datasetReply = obj
is DatasetReplyMessage -> datasetReply = obj
}
}
}

View file

@ -8,7 +8,14 @@ import retrofit2.Call
import retrofit2.Callback
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> {
override fun onResponse(call: Call<T>, response: Response<T>) {

View file

@ -5,8 +5,8 @@ import android.os.PowerManager
import android.os.SystemClock
import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.plugin.ActivePlugin
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.messages.AuthReplyMessage
import info.nightscout.plugins.sync.tidepool.messages.AuthRequestMessage
@ -37,11 +37,12 @@ class TidepoolUploader @Inject constructor(
private val rh: ResourceHelper,
private val sp: SP,
private val uploadChunk: UploadChunk,
private val activePlugin: ActivePlugin,
private val dateUtil: DateUtil,
private val receiverDelegate: ReceiverDelegate,
private val config: Config
) {
private val isAllowed get() = receiverDelegate.allowed
private var wl: PowerManager.WakeLock? = null
companion object {
@ -57,7 +58,7 @@ class TidepoolUploader @Inject constructor(
private var session: Session? = null
enum class ConnectionStatus {
DISCONNECTED, CONNECTING, CONNECTED, FAILED
BLOCKED, DISCONNECTED, CONNECTING, CONNECTED, FAILED
}
var connectionStatus: ConnectionStatus = ConnectionStatus.DISCONNECTED
@ -70,7 +71,7 @@ class TidepoolUploader @Inject constructor(
val client = OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(InfoInterceptor(TidepoolUploader::class.java.name, aapsLogger))
.addInterceptor(InfoInterceptor(aapsLogger))
.build()
retrofit = Retrofit.Builder()
@ -87,7 +88,6 @@ class TidepoolUploader @Inject constructor(
return Session(AuthRequestMessage.getAuthRequestHeader(sp), SESSION_TOKEN_HEADER, service)
}
// TODO: call on preference change
fun resetInstance() {
retrofit = null
aapsLogger.debug(LTag.TIDEPOOL, "Instance reset")
@ -96,6 +96,11 @@ class TidepoolUploader @Inject constructor(
@Synchronized
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) {
aapsLogger.debug(LTag.TIDEPOOL, "Already connected")
return
@ -109,12 +114,15 @@ class TidepoolUploader @Inject constructor(
rxBus.send(EventTidepoolStatus(("Connecting")))
val call = session?.service?.getLogin(authHeader)
call?.enqueue(TidepoolCallback<AuthReplyMessage>(aapsLogger, rxBus, session!!, "Login", {
startSession(session!!, doUpload)
}, {
connectionStatus = ConnectionStatus.FAILED
releaseWakeLock()
}))
call?.enqueue(TidepoolCallback<AuthReplyMessage>(
aapsLogger, rxBus, session!!, "Login",
{
startSession(session!!, doUpload)
}, {
connectionStatus = ConnectionStatus.FAILED
releaseWakeLock()
})
)
return
} else {
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 {
val call = session.service?.getLogin(it)
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),
"Failed to log into Tidepool.\nCheck that your user name and password are correct."
)
}))
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), "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")
@ -154,39 +160,41 @@ class TidepoolUploader @Inject constructor(
session.authReply!!.userid!!, "AAPS", 1
)
datasetCall.enqueue(TidepoolCallback<List<DatasetReplyMessage>>(aapsLogger, rxBus, session, "Get Open Datasets", {
if (session.datasetReply == null) {
rxBus.send(EventTidepoolStatus(("Creating new dataset")))
val call = session.service.openDataSet(
session.token!!, session.authReply!!.userid!!,
OpenDatasetRequestMessage(config, dateUtil).getBody()
)
call.enqueue(TidepoolCallback<DatasetReplyMessage>(aapsLogger, rxBus, session, "Open New Dataset", {
connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("New dataset OK")))
if (doUpload) doUpload()
else
releaseWakeLock()
datasetCall.enqueue(
TidepoolCallback<List<DatasetReplyMessage>>(
aapsLogger, rxBus, session, "Get Open Datasets",
{
if (session.datasetReply == null) {
rxBus.send(EventTidepoolStatus(("Creating new dataset")))
val call = session.service.openDataSet(session.token!!, session.authReply!!.userid!!, OpenDatasetRequestMessage(config, dateUtil).getBody())
call.enqueue(TidepoolCallback<DatasetReplyMessage>(
aapsLogger, rxBus, session, "Open New Dataset",
{
connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("New dataset OK")))
if (doUpload) doUpload()
else releaseWakeLock()
}, {
rxBus.send(EventTidepoolStatus(("New dataset FAILED")))
connectionStatus = ConnectionStatus.FAILED
releaseWakeLock()
})
)
} else {
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`.
// ie, do the openDataSet conditionally, and then do `doUpload` either way.
connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("Appending to existing dataset")))
if (doUpload) doUpload()
else releaseWakeLock()
}
}, {
rxBus.send(EventTidepoolStatus(("New dataset FAILED")))
connectionStatus = ConnectionStatus.FAILED
releaseWakeLock()
}))
} else {
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`.
// ie, do the openDataSet conditionally, and then do `doUpload` either way.
connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("Appending to existing dataset")))
if (doUpload) doUpload()
else
connectionStatus = ConnectionStatus.FAILED
rxBus.send(EventTidepoolStatus(("Open dataset FAILED")))
releaseWakeLock()
}
}, {
connectionStatus = ConnectionStatus.FAILED
rxBus.send(EventTidepoolStatus(("Open dataset FAILED")))
releaseWakeLock()
}))
})
)
} else {
aapsLogger.error("Got login response but cannot determine userId - cannot proceed")
connectionStatus = ConnectionStatus.FAILED
@ -197,6 +205,11 @@ class TidepoolUploader @Inject constructor(
@Synchronized
fun doUpload() {
if (!isAllowed) {
connectionStatus = ConnectionStatus.BLOCKED
aapsLogger.debug(LTag.TIDEPOOL, "Blocked by connectivity settings")
return
}
session.let { session ->
if (session == null) {
aapsLogger.error("Session is null, cannot proceed")
@ -225,16 +238,20 @@ class TidepoolUploader @Inject constructor(
rxBus.send(EventTidepoolStatus(("Uploading")))
if (session.service != null && session.token != null && session.datasetReply != null) {
val call = session.service.doUpload(session.token!!, session.datasetReply!!.getUploadId()!!, body)
call.enqueue(TidepoolCallback<UploadReplyMessage>(aapsLogger, rxBus, session, "Data Upload", {
uploadChunk.setLastEnd(session.end)
rxBus.send(EventTidepoolStatus(("Upload completed OK")))
releaseWakeLock()
uploadNext()
}, {
connectionStatus = ConnectionStatus.FAILED
rxBus.send(EventTidepoolStatus(("Upload FAILED")))
releaseWakeLock()
}))
call.enqueue(TidepoolCallback<UploadReplyMessage>(
aapsLogger, rxBus, session, "Data Upload",
{
uploadChunk.setLastEnd(session.end)
connectionStatus = ConnectionStatus.CONNECTED
rxBus.send(EventTidepoolStatus(("Upload completed OK")))
releaseWakeLock()
uploadNext()
}, {
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Upload FAILED")))
releaseWakeLock()
})
)
}
}
}
@ -242,7 +259,12 @@ class TidepoolUploader @Inject constructor(
}
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)
aapsLogger.debug(LTag.TIDEPOOL, "Restarting doUpload. Last: " + dateUtil.dateAndTimeString(uploadChunk.getLastEnd()))
doUpload()
@ -253,15 +275,18 @@ class TidepoolUploader @Inject constructor(
if (session?.datasetReply?.id != null) {
extendWakeLock(60000)
val call = session!!.service?.deleteDataSet(session!!.token!!, session!!.datasetReply!!.id!!)
call?.enqueue(TidepoolCallback(aapsLogger, rxBus, session!!, "Delete Dataset", {
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Dataset removed OK")))
releaseWakeLock()
}, {
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Dataset remove FAILED")))
releaseWakeLock()
}))
call?.enqueue(TidepoolCallback(
aapsLogger, rxBus, session!!, "Delete Dataset",
{
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Dataset removed OK")))
releaseWakeLock()
}, {
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("Dataset remove FAILED")))
releaseWakeLock()
})
)
} else {
aapsLogger.error("Got login response but cannot determine datasetId - cannot proceed")
}
@ -278,15 +303,19 @@ class TidepoolUploader @Inject constructor(
requireNotNull(userId)
extendWakeLock(60000)
val call = session.service?.deleteAllData(token, userId)
call?.enqueue(TidepoolCallback(aapsLogger, rxBus, session, "Delete all data", {
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("All data removed OK")))
releaseWakeLock()
}, {
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("All data remove FAILED")))
releaseWakeLock()
}))
call?.enqueue(
TidepoolCallback(
aapsLogger, rxBus, session, "Delete all data",
{
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("All data removed OK")))
releaseWakeLock()
}, {
connectionStatus = ConnectionStatus.DISCONNECTED
rxBus.send(EventTidepoolStatus(("All data remove FAILED")))
releaseWakeLock()
})
)
} catch (e: IllegalArgumentException) {
aapsLogger.error("Got login response but cannot determine userId - cannot proceed")
}
@ -307,13 +336,12 @@ class TidepoolUploader @Inject constructor(
@Synchronized
private fun releaseWakeLock() {
wl?.let {
if (it.isHeld) {
if (it.isHeld)
try {
it.release()
} catch (e: Exception) {
aapsLogger.error("Error releasing wakelock: $e")
}
}
}
}

View file

@ -41,21 +41,21 @@ class UploadChunk @Inject constructor(
private val maxUploadSize = T.days(7).msecs() // don't change this
fun getNext(session: Session?): String? {
if (session == null)
return null
session ?: return null
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)
if (result.length < 3) {
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
}
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))
if (end <= start) {
@ -69,16 +69,11 @@ class UploadChunk @Inject constructor(
val records = LinkedList<BaseElement>()
if (sp.getBoolean(R.string.key_tidepool_upload_bolus, true))
records.addAll(getTreatments(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_bg, true))
records.addAll(getBloodTests(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_tbr, true))
records.addAll(getBasals(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_cgm, true))
records.addAll(getBgReadings(start, end))
if (sp.getBoolean(R.string.key_tidepool_upload_profile, true))
records.addAll(getProfiles(start, end))
records.addAll(getTreatments(start, end))
records.addAll(getBloodTests(start, end))
records.addAll(getBasals(start, end))
records.addAll(getBgReadings(start, end))
records.addAll(getProfiles(start, end))
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> {
val result = LinkedList<BaseElement>()
repository.getBolusesDataFromTimeToTime(start, end, true)

View file

@ -7,7 +7,7 @@ import info.nightscout.interfaces.profile.Profile
import info.nightscout.shared.utils.DateUtil
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) {
internal var timestamp: Long = 0 // not exposed

View file

@ -35,14 +35,21 @@ class ProfileElement(ps: EffectiveProfileSwitch, serialNumber: String, dateUtil:
init {
type = "pumpSettings"
val profile: Profile = ProfileSealed.EPS(ps)
for (br in profile.getBasalValues())
basalSchedules.Normal.add(BasalRate(br.timeAsSeconds * 1000, br.value))
for (target in profile.getSingleTargetsMgdl())
bgTargets.Normal.add(Target(target.timeAsSeconds * 1000, target.value.toInt()))
for (ic in profile.getIcsValues())
carbRatios.Normal.add(Ratio(ic.timeAsSeconds * 1000, ic.value.toInt()))
for (isf in profile.getIsfsMgdlValues())
insulinSensitivities.Normal.add(Ratio(isf.timeAsSeconds * 1000, isf.value.toInt()))
// for (br in profile.getBasalValues())
// basalSchedules.Normal.add(BasalRate(br.timeAsSeconds * 1000, br.value))
// for (target in profile.getSingleTargetsMgdl())
// bgTargets.Normal.add(Target(target.timeAsSeconds * 1000, target.value.toInt()))
// for (ic in profile.getIcsValues())
// carbRatios.Normal.add(Ratio(ic.timeAsSeconds * 1000, ic.value.toInt()))
// for (isf in profile.getIsfsMgdlValues())
// 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(

View file

@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".sync.nsShared.NSClientFragment">
tools:context="info.nightscout.plugins.sync.nsShared.NSClientFragment">
<LinearLayout
android:layout_width="match_parent"

View file

@ -1,82 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:context="info.nightscout.plugins.sync.tidepool.TidepoolFragment">
<TextView
android:id="@+id/status"
android:layout_width="0dp"
<LinearLayout
android:layout_width="match_parent"
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" />
android:layout_marginBottom="10dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/login"
style="@style/GrayButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/login"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="@string/status"
tools:ignore="RtlHardcoded" />
<com.google.android.material.button.MaterialButton
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" />
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="normal|bold" />
<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" />
</LinearLayout>
<ScrollView
android:id="@+id/logscrollview"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
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">
android:id="@+id/log_scrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp">
<TextView
android:id="@+id/log"
android:layout_width="match_parent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="-- logs --"
tools:ignore="HardcodedText" />
android:text="" />
</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_temporary_basal_last_synced_id" translatable="false">ns_temporary_basal_last_synced_id</string>
<string name="key_ns_extended_bolus_last_synced_id" translatable="false">ns_extended_bolus_last_synced_id</string>
<string name="key_ns_profile_switch_last_synced_id" translatable="false">profile_switch_last_synced_id</string>
<string name="key_ns_profile_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_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>
@ -48,7 +48,7 @@
<!-- NSClient -->
<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="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_dev_servers" translatable="false">tidepool_dev_servers</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_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="title_tidepool_username">Login User Name</string>
@ -187,5 +180,7 @@
<!-- DataBroadcast-->
<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>

View file

@ -178,14 +178,13 @@
<androidx.preference.PreferenceScreen
android:key="nsclient_advanced"
android:title="@string/advancedsettings_title">
android:title="@string/advanced_settings_title">
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_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
android:defaultValue="true"

View file

@ -9,7 +9,7 @@
app:initialExpandedChildrenCount="0">
<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:inputType="textUri"
android:key="@string/key_nsclientinternal_url"
@ -24,7 +24,13 @@
android:key="@string/key_ns_client_token"
android:title="@string/nsclient_token_title"
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
android:key="@string/ns_sync_options"
@ -178,14 +184,13 @@
<androidx.preference.PreferenceScreen
android:key="nsclient_advanced"
android:title="@string/advancedsettings_title">
android:title="@string/advanced_settings_title">
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_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
android:defaultValue="true"

View file

@ -24,49 +24,57 @@
android:key="@string/key_tidepool_test_login"
android:title="@string/title_tidepool_test_login" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_cgm"
android:title="@string/tidepool_upload_cgm" />
<androidx.preference.PreferenceScreen
android:key="@string/connection_settings_title"
android:title="@string/connection_settings_title">
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_bolus"
android:title="@string/tidepool_upload_bolus" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_cellular"
android:title="@string/ns_cellular" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_bg"
android:title="@string/tidepool_upload_bg" />
<SwitchPreference
android:defaultValue="true"
android:dependency="@string/key_ns_cellular"
android:key="@string/key_ns_allow_roaming"
android:title="@string/ns_allow_roaming" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_tbr"
android:title="@string/tidepool_upload_tbr" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_wifi"
android:title="@string/ns_wifi" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/key_tidepool_upload_profile"
android:title="@string/tidepool_upload_profile" />
<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" />
<CheckBoxPreference
android:defaultValue="true"
android:enabled="false"
android:key="@string/key_tidepool_dev_servers"
android:summary="@string/summary_tidepool_dev_servers"
android:title="@string/title_tidepool_dev_servers" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_battery"
android:title="@string/ns_battery" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_tidepool_only_while_charging"
android:summary="Upload data only when charging"
android:title="Only when charging" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_charging"
android:title="@string/ns_charging" />
<CheckBoxPreference
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" />
</androidx.preference.PreferenceScreen>
<androidx.preference.PreferenceScreen
android:key="nsclient_advanced"
android:title="@string/advanced_settings_title">
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_tidepool_dev_servers"
android:summary="@string/summary_tidepool_dev_servers"
android:title="@string/title_tidepool_dev_servers" />
</androidx.preference.PreferenceScreen>
</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
import androidx.work.WorkManager
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.core.extensions.fromConstant
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.BolusCalculatorResult
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.sync.DataSyncSelector
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.workflow.WorkerClasses
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.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse
@ -45,7 +49,7 @@ import org.mockito.internal.verification.Times
@Suppress("SpellCheckingInspection")
internal class NSClientV3PluginTest : TestBaseWithProfile() {
@Mock lateinit var nsClientReceiverDelegate: NsClientReceiverDelegate
@Mock lateinit var receiverDelegate: ReceiverDelegate
@Mock lateinit var uiInteraction: UiInteraction
@Mock lateinit var dataSyncSelector: DataSyncSelector
@Mock lateinit var nsAndroidClient: NSAndroidClient
@ -54,6 +58,10 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
@Mock lateinit var xDripBroadcast: XDripBroadcast
@Mock lateinit var virtualPump: VirtualPump
@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 sut: NSClientV3Plugin
@ -65,11 +73,12 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
@BeforeEach
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 =
NSClientV3Plugin(
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
`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.sync.DataSyncSelector
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
@ -27,6 +28,7 @@ internal class DataSyncWorkerTest : TestBase() {
@Mock lateinit var activePlugin: ActivePlugin
@Mock lateinit var nsClient: NsClient
@Mock lateinit var rxBus: RxBus
@Mock lateinit var nsClientV3Plugin: NSClientV3Plugin
private lateinit var sut: DataSyncWorker
@ -38,6 +40,7 @@ internal class DataSyncWorkerTest : TestBase() {
it.dataSyncSelector = dataSyncSelector
it.activePlugin = activePlugin
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.ui.UiInteraction
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.extensions.toNSSvgV3
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.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
@ -49,7 +51,6 @@ internal class LoadBgWorkerTest : TestBase() {
@Mock lateinit var workerClasses: WorkerClasses
@Mock lateinit var sp: SP
@Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var rxBus: RxBus
@Mock lateinit var dateUtil: DateUtil
@Mock lateinit var nsAndroidClient: NSAndroidClient
@Mock lateinit var rh: ResourceHelper
@ -62,9 +63,11 @@ internal class LoadBgWorkerTest : TestBase() {
@Mock lateinit var nsClientSource: NSClientSource
@Mock lateinit var workManager: WorkManager
@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 nsClientReceiverDelegate: NsClientReceiverDelegate
private lateinit var receiverDelegate: ReceiverDelegate
private lateinit var dataWorkerStorage: DataWorkerStorage
private lateinit var sut: LoadBgWorker
@ -95,10 +98,11 @@ internal class LoadBgWorkerTest : TestBase() {
Mockito.`when`(dateUtil.now()).thenReturn(now)
Mockito.`when`(nsClientSource.isEnabled()).thenReturn(true)
dataWorkerStorage = DataWorkerStorage(context)
nsClientReceiverDelegate = NsClientReceiverDelegate(rxBus, rh, sp, receiverStatusStore)
receiverDelegate = ReceiverDelegate(rxBus, rh, sp, receiverStatusStore, aapsSchedulers, fabricPrivacy)
nsClientV3Plugin = NSClientV3Plugin(
injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy, sp, nsClientReceiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector,
profileFunction, repository
injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy,
sp, receiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, profileFunction, repository,
nsDeviceStatusHandler, workManager, workerClasses, dataWorkerStorage, nsClientSource
)
nsClientV3Plugin.newestDataOnServer = LastModified(LastModified.Collections())
}
@ -121,7 +125,7 @@ internal class LoadBgWorkerTest : TestBase() {
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Assertions.assertTrue(result.outputData.getString("Result") == "Load not enabled")
Mockito.verify(workManager, Mockito.times(1)).enqueueUniqueWork(
eq(NSClientV3Plugin.JOB_NAME),
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)
@ -135,13 +139,13 @@ internal class LoadBgWorkerTest : TestBase() {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = 0L // first load
nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000
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()
Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries)
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(NSClientV3Plugin.JOB_NAME),
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)
@ -172,12 +176,12 @@ internal class LoadBgWorkerTest : TestBase() {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = 0L // first load
nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000
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()
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(NSClientV3Plugin.JOB_NAME),
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)
@ -194,13 +198,13 @@ internal class LoadBgWorkerTest : TestBase() {
nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000
nsClientV3Plugin.newestDataOnServer?.collections?.entries = now - 2000
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()
Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries)
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(NSClientV3Plugin.JOB_NAME),
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)