NSCv3: websockets support

This commit is contained in:
Milos Kozak 2023-01-23 23:50:32 +01:00
parent 9757428577
commit 89ad9e21dd
29 changed files with 576 additions and 210 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -116,7 +116,7 @@ class NSClientSourcePlugin @Inject constructor(
return TransactionGlucoseValue( return TransactionGlucoseValue(
timestamp = sgv.date ?: throw InvalidParameterException(), timestamp = sgv.date ?: throw InvalidParameterException(),
value = sgv.sgv, value = sgv.sgv,
noise = sgv.noise?.toDouble(), noise = sgv.noise,
raw = sgv.filtered, raw = sgv.filtered,
trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction?.nsName), trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction?.nsName),
nightscoutId = sgv.identifier, nightscoutId = sgv.identifier,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,6 +33,7 @@ import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsShared.events.EventNSConnectivityOptionChanged
import info.nightscout.plugins.sync.nsclient.NSClientPlugin import info.nightscout.plugins.sync.nsclient.NSClientPlugin
import info.nightscout.plugins.sync.nsclient.acks.NSAddAck import info.nightscout.plugins.sync.nsclient.acks.NSAddAck
import info.nightscout.plugins.sync.nsclient.acks.NSAuthAck import info.nightscout.plugins.sync.nsclient.acks.NSAuthAck
@ -140,13 +141,21 @@ class NSClientService : DaggerService() {
.subscribe({ event: EventPreferenceChange -> .subscribe({ event: EventPreferenceChange ->
if (event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)) || if (event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)) ||
event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_api_secret)) || event.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_api_secret)) ||
event.isChanged(rh.gs(R.string.key_ns_client_paused)) event.isChanged(rh.gs(R.string.key_ns_paused))
) { ) {
latestDateInReceivedData = 0 latestDateInReceivedData = 0
destroy() destroy()
initialize() initialize()
} }
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNSConnectivityOptionChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
latestDateInReceivedData = 0
destroy()
initialize()
}, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAppExit::class.java) .toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -218,10 +227,10 @@ class NSClientService : DaggerService() {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString() if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString()
rxBus.send(EventNSClientStatus("Initializing")) rxBus.send(EventNSClientStatus("Initializing"))
if (!nsClientPlugin.isAllowed) { if (nsClientPlugin.isAllowed != true) {
rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason)) rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason))
rxBus.send(EventNSClientStatus(nsClientPlugin.blockingReason)) rxBus.send(EventNSClientStatus(nsClientPlugin.blockingReason))
} else if (sp.getBoolean(R.string.key_ns_client_paused, false)) { } else if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused")) rxBus.send(EventNSClientNewLog("NSCLIENT", "paused"))
rxBus.send(EventNSClientStatus("Paused")) rxBus.send(EventNSClientStatus("Paused"))
} else if (!nsEnabled) { } else if (!nsEnabled) {
@ -230,9 +239,7 @@ class NSClientService : DaggerService() {
} else if (nsURL != "" && (nsURL.lowercase(Locale.getDefault()).startsWith("https://"))) { } else if (nsURL != "" && (nsURL.lowercase(Locale.getDefault()).startsWith("https://"))) {
try { try {
rxBus.send(EventNSClientStatus("Connecting ...")) rxBus.send(EventNSClientStatus("Connecting ..."))
val opt = IO.Options() val opt = IO.Options().also { it.forceNew = true }
opt.forceNew = true
opt.reconnection = true
socket = IO.socket(nsURL, opt).also { socket -> socket = IO.socket(nsURL, opt).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnect) socket.on(Socket.EVENT_CONNECT, onConnect)
socket.on(Socket.EVENT_DISCONNECT, onDisconnect) socket.on(Socket.EVENT_DISCONNECT, onDisconnect)

View file

@ -3,6 +3,7 @@ package info.nightscout.plugins.sync.nsclientV3
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.os.SystemClock
import android.text.Spanned import android.text.Spanned
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
@ -16,26 +17,34 @@ import com.google.gson.GsonBuilder
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.database.ValueWrapper import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.interfaces.TraceableDBEntry import info.nightscout.database.entities.interfaces.TraceableDBEntry
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HtmlHelper import info.nightscout.interfaces.utils.HtmlHelper
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.NSClientFragment import info.nightscout.plugins.sync.nsShared.NSClientFragment
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.events.EventNSClientResend import info.nightscout.plugins.sync.nsShared.events.EventNSClientResend
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsShared.events.EventNSConnectivityOptionChanged
import info.nightscout.plugins.sync.nsclient.NsClientReceiverDelegate import info.nightscout.plugins.sync.nsclient.NsClientReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolus import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolus
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolusWizard import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolusWizard
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSCarbs import info.nightscout.plugins.sync.nsclientV3.extensions.toNSCarbs
@ -49,16 +58,17 @@ import info.nightscout.plugins.sync.nsclientV3.extensions.toNSSvgV3
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryBasal import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryBasal
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryTarget import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryTarget
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTherapyEvent import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTherapyEvent
import info.nightscout.plugins.sync.nsclientV3.workers.DataSyncWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessFoodWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessTreatmentsWorker
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventChargingState import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNetworkChange
import info.nightscout.rx.events.EventNewBG
import info.nightscout.rx.events.EventNewHistoryData import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventSWSyncStatus import info.nightscout.rx.events.EventSWSyncStatus
@ -66,6 +76,10 @@ import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.NSAndroidClientImpl import info.nightscout.sdk.NSAndroidClientImpl
import info.nightscout.sdk.interfaces.NSAndroidClient import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.mapper.toNSDeviceStatus
import info.nightscout.sdk.mapper.toNSFood
import info.nightscout.sdk.mapper.toNSSgvV3
import info.nightscout.sdk.mapper.toNSTreatment
import info.nightscout.sdk.remotemodel.LastModified import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
@ -73,15 +87,19 @@ import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import io.socket.client.Ack
import io.socket.client.IO
import io.socket.client.Socket
import io.socket.emitter.Emitter
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.util.concurrent.Executors import org.json.JSONArray
import java.util.concurrent.ScheduledFuture import org.json.JSONObject
import java.util.concurrent.TimeUnit import java.net.URISyntaxException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -102,7 +120,12 @@ class NSClientV3Plugin @Inject constructor(
private val uiInteraction: UiInteraction, private val uiInteraction: UiInteraction,
private val dataSyncSelector: DataSyncSelector, private val dataSyncSelector: DataSyncSelector,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val repository: AppRepository private val repository: AppRepository,
private val nsDeviceStatusHandler: NSDeviceStatusHandler,
private val workManager: WorkManager,
private val workerClasses: WorkerClasses,
private val dataWorkerStorage: DataWorkerStorage,
private val nsClientSource: NSClientSource
) : NsClient, Sync, PluginBase( ) : NsClient, Sync, PluginBase(
PluginDescription() PluginDescription()
.mainType(PluginType.SYNC) .mainType(PluginType.SYNC)
@ -118,7 +141,6 @@ class NSClientV3Plugin @Inject constructor(
companion object { companion object {
val JOB_NAME: String = this::class.java.simpleName val JOB_NAME: String = this::class.java.simpleName
val REFRESH_INTERVAL = T.secs(30).msecs()
const val RECORDS_TO_LOAD = 500L const val RECORDS_TO_LOAD = 500L
} }
@ -130,14 +152,16 @@ class NSClientV3Plugin @Inject constructor(
override val status override val status
get() = get() =
when { when {
sp.getBoolean(R.string.key_ns_client_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused) sp.getBoolean(R.string.key_ns_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused)
isAllowed.not() -> blockingReason isAllowed.not() -> blockingReason
lastOperationError != null -> rh.gs(info.nightscout.core.ui.R.string.error) sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && wsConnected -> "WS: " + rh.gs(info.nightscout.shared.R.string.connected)
nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected) sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && !wsConnected -> "WS: " + rh.gs(R.string.not_connected)
workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working) lastOperationError != null -> rh.gs(info.nightscout.core.ui.R.string.error)
nsAndroidClient?.lastStatus?.apiPermissions?.isFull() == true -> rh.gs(info.nightscout.shared.R.string.connected) nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected)
nsAndroidClient?.lastStatus?.apiPermissions?.isRead() == true -> rh.gs(R.string.read_only) workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working)
else -> rh.gs(info.nightscout.core.ui.R.string.unknown) 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 var lastOperationError: String? = null
@ -161,26 +185,30 @@ class NSClientV3Plugin @Inject constructor(
) )
) )
setClient() setClient("START")
nsClientReceiverDelegate.grabReceiversState() nsClientReceiverDelegate.grabReceiversState()
disposable += rxBus disposable += rxBus
.toObservable(EventNetworkChange::class.java) .toObservable(EventNSConnectivityOptionChanged::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ ev -> .subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev) rxBus.send(EventNSClientNewLog("● CONNECTIVITY CHANGE", ev.blockingReason))
setClient() setClient("CONNECTIVITY_CHANGE")
rxBus.send(EventNSClientUpdateGUI()) if (isAllowed) executeLoop("CONNECTIVITY_CHANGE", forceNew = false)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ ev -> .subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev) if (ev.isChanged(rh.gs(R.string.key_ns_client_token)) ||
if (ev.isChanged(rh.gs(R.string.key_ns_client_token)) || ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url))) ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)) ||
setClient() ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_ns_use_ws)) ||
ev.isChanged(rh.gs(R.string.key_ns_paused))
)
setClient("SETTING CHANGE")
if (ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_local_profile_last_change))) if (ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_local_profile_last_change)))
delayAndScheduleExecution("PROFILE_CHANGE") executeUpload("PROFILE_CHANGE", forceNew = true)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAppExit::class.java) .toObservable(EventAppExit::class.java)
@ -193,47 +221,55 @@ class NSClientV3Plugin @Inject constructor(
addToLog(event) addToLog(event)
aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText) aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventChargingState::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ ev ->
nsClientReceiverDelegate.onStatusEvent(ev)
rxBus.send(EventNSClientUpdateGUI())
}, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNSClientResend::class.java) .toObservable(EventNSClientResend::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException) .subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ delayAndScheduleExecution("NEW_BG") }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNewHistoryData::class.java) .toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ delayAndScheduleExecution("NEW_DATA") }, fabricPrivacy::logException) .subscribe({ executeUpload("NEW_DATA", forceNew = false) }, fabricPrivacy::logException)
runLoop = Runnable { runLoop = Runnable {
handler.postDelayed(runLoop, REFRESH_INTERVAL) var refreshInterval = T.mins(5).msecs()
repository.getLastGlucoseValueWrapped().blockingGet().let { repository.getLastGlucoseValueWrapped().blockingGet().let {
// if last value is older than 5 min or there is no bg // if last value is older than 5 min or there is no bg
if (it is ValueWrapper.Existing) { if (it is ValueWrapper.Existing) {
if (it.value.timestamp < dateUtil.now() - T.mins(5).plus(T.secs(20)).msecs()) if (it.value.timestamp < dateUtil.now() - T.mins(5).plus(T.secs(20)).msecs()) {
refreshInterval = T.mins(1).msecs()
executeLoop("MAIN_LOOP", forceNew = false) executeLoop("MAIN_LOOP", forceNew = false)
else {
if (isAllowed) rxBus.send(EventNSClientNewLog("RECENT", "No need to load"))
} }
} else executeLoop("MAIN_LOOP", forceNew = false) } else executeLoop("MAIN_LOOP", forceNew = false)
} }
handler.postDelayed(runLoop, refreshInterval)
rxBus.send(EventNSClientNewLog("● TICK", ""))
}
handler.postDelayed(runLoop, T.mins(2).msecs())
}
fun scheduleIrregularExecution(refreshToken: Boolean = false) {
if (refreshToken) {
handler.post { executeLoop("REFRESH TOKEN", forceNew = true) }
return
}
if (config.NSCLIENT || nsClientSource.isEnabled()) {
var origin = "5_MIN_AFTER_BG"
var forceNew = true
var toTime = lastLoadedSrvModified.collections.entries + T.mins(5).plus(T.secs(10)).msecs()
if (toTime < dateUtil.now()) {
toTime = dateUtil.now() + T.mins(1).plus(T.secs(0)).msecs()
origin = "1_MIN_OLD_DATA"
forceNew = false
}
handler.postDelayed({ executeLoop(origin, forceNew = forceNew) }, toTime - dateUtil.now())
rxBus.send(EventNSClientNewLog("● NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
} }
handler.postDelayed(runLoop, REFRESH_INTERVAL)
executeLoop("START", forceNew = false)
} }
override fun onStop() { override fun onStop() {
// context.applicationContext.unbindService(mConnection)
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
disposable.clear() disposable.clear()
socket?.disconnect()
super.onStop() super.onStop()
} }
@ -257,16 +293,269 @@ class NSClientV3Plugin @Inject constructor(
} }
} }
private fun setClient() { private fun setClient(reason: String) {
nsAndroidClient = NSAndroidClientImpl( nsAndroidClient = NSAndroidClientImpl(
baseUrl = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").lowercase().replace("https://", "").replace(Regex("/$"), ""), baseUrl = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").lowercase().replace("https://", "").replace(Regex("/$"), ""),
accessToken = sp.getString(R.string.key_ns_client_token, ""), accessToken = sp.getString(R.string.key_ns_client_token, ""),
context = context, context = context,
logging = true logging = true
) )
if (wsConnected)
socket?.disconnect()
SystemClock.sleep(2000)
initializeWebSockets(reason)
rxBus.send(EventSWSyncStatus(status)) rxBus.send(EventSWSyncStatus(status))
} }
/**********************
WS code
**********************/
private var connectCounter = 0
private var socket: 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
val url = sp.getString(info.nightscout.core.utils.R.string.key_nsclientinternal_url, "").lowercase().replace(Regex("/$"), "") + "/storage"
if (!isAllowed) {
rxBus.send(EventNSClientNewLog("● WS", blockingReason))
} else if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("● WS", "paused"))
} else if (url != "") {
try {
//rxBus.send(EventNSClientStatus("Connecting ..."))
val opt = IO.Options().also {
it.forceNew = true
//it.webSocketFactory = nsAndroidClient.
}
socket = IO.socket(url, opt).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnect)
socket.on(Socket.EVENT_DISCONNECT, onDisconnect)
rxBus.send(EventNSClientNewLog("► WS", "do connect $reason"))
socket.connect()
socket.on("create", onDataCreateUpdate)
socket.on("update", onDataCreateUpdate)
socket.on("delete", onDataDelete)
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"))
}
} else {
rxBus.send(EventNSClientNewLog("● WS", "No NS URL specified"))
}
}
private val onConnect = Emitter.Listener {
connectCounter++
val socketId = socket?.id() ?: "NULL"
rxBus.send(EventNSClientNewLog("◄ WS", "connect #$connectCounter event. ID: $socketId"))
if (socket != null) {
val authMessage = JSONObject().also {
it.put("accessToken", sp.getString(R.string.key_ns_client_token, ""))
it.put("collections", JSONArray(arrayOf<String>("devicestatus", "entries", "profile", "treatments", "foods", "settings")))
}
rxBus.send(EventNSClientNewLog("► WS", "requesting auth"))
socket?.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 onDisconnect = Emitter.Listener { args ->
aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: ${args[0]}")
rxBus.send(EventNSClientNewLog("◄ WS", "disconnect event"))
wsConnected = false
initialLoadFinished = false
socket = 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: JSONObject
try {
data = args[0] as JSONObject
handleAnnouncement(data)
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private val onAlarm = Emitter.Listener { args ->
/*
{
"level":1,
"title":"Warning HIGH",
"message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g",
"eventName":"high",
"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true},
"pushoverSound":"climb",
"debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}},
"group":"default",
"key":"simplealarms_1"
}
*/
val data: JSONObject
try {
data = args[0] as JSONObject
handleAlarm(data)
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private val onUrgentAlarm = Emitter.Listener { args: Array<Any> ->
val data: JSONObject
try {
data = args[0] as JSONObject
handleUrgentAlarm(data)
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private val onClearAlarm = Emitter.Listener { args ->
/*
{
"clear":true,
"title":"All Clear",
"message":"default - Urgent was ack'd",
"group":"default"
}
*/
val data: JSONObject
try {
data = args[0] as JSONObject
rxBus.send(EventNSClientNewLog("CLEARALARM", "received"))
rxBus.send(EventDismissNotification(Notification.NS_ALARM))
rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
aapsLogger.debug(LTag.NSCLIENT, data.toString())
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
}
}
private fun handleAnnouncement(announcement: JSONObject) {
val defaultVal = config.NSCLIENT
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_announcements, defaultVal)) {
val nsAlarm = NSAlarm(announcement)
uiInteraction.addNotificationWithAction(injector, nsAlarm)
rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", JsonHelper.safeGetString(announcement, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, announcement.toString())
}
}
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)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm)
}
rxBus.send(EventNSClientNewLog("ALARM", JsonHelper.safeGetString(alarm, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
}
}
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)
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm)
}
rxBus.send(EventNSClientNewLog("URGENTALARM", JsonHelper.safeGetString(alarm, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
}
}
/**********************
WS code end
**********************/
private fun addToLog(ev: EventNSClientNewLog) { private fun addToLog(ev: EventNSClientNewLog) {
synchronized(listLog) { synchronized(listLog) {
listLog.add(ev) listLog.add(ev)
@ -292,12 +581,15 @@ class NSClientV3Plugin @Inject constructor(
} }
override fun resend(reason: String) { 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) { override fun pause(newState: Boolean) {
sp.putBoolean(R.string.key_ns_client_paused, newState) sp.putBoolean(R.string.key_ns_paused, newState)
rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_client_paused))) rxBus.send(EventPreferenceChange(rh.gs(R.string.key_ns_paused)))
} }
override fun detectedNsVersion(): String? = nsAndroidClient?.lastStatus?.version override fun detectedNsVersion(): String? = nsAndroidClient?.lastStatus?.version
@ -337,6 +629,7 @@ class NSClientV3Plugin @Inject constructor(
override fun resetToFullSync() { override fun resetToFullSync() {
firstLoadContinueTimestamp = LastModified(LastModified.Collections()) firstLoadContinueTimestamp = LastModified(LastModified.Collections())
lastLoadedSrvModified = LastModified(LastModified.Collections()) lastLoadedSrvModified = LastModified(LastModified.Collections())
initialLoadFinished = false
storeLastLoadedSrvModified() storeLastLoadedSrvModified()
} }
@ -355,15 +648,15 @@ class NSClientV3Plugin @Inject constructor(
val data = (dataPair as DataSyncSelector.PairProfileStore).value val data = (dataPair as DataSyncSelector.PairProfileStore).value
scope.launch { scope.launch {
try { try {
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} $data $progress")) rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} <i>$data</i> $progress"))
nsAndroidClient?.createProfileStore(data)?.let { result -> nsAndroidClient?.createProfileStore(data)?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ProfileStore")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ProfileStore"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ProfileStore")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ProfileStore"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "ProfileStore")) rxBus.send(EventNSClientNewLog("ERROR", "ProfileStore"))
return@launch return@launch
} }
} }
@ -385,15 +678,15 @@ class NSClientV3Plugin @Inject constructor(
val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toNSDeviceStatus() val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toNSDeviceStatus()
scope.launch { scope.launch {
try { try {
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress")) rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"))
nsAndroidClient?.createDeviceStatus(data)?.let { result -> nsAndroidClient?.createDeviceStatus(data)?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName} ${result.identifier}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName} ${result.identifier}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -426,24 +719,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send( rxBus.send(
EventNSClientNewLog( EventNSClientNewLog(
when (operation) { when (operation) {
Operation.CREATE -> "ADD $collection" Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection" Operation.UPDATE -> "UPDATE $collection"
}, },
when (operation) { when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress" Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress" Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
} }
) )
) )
call?.let { it(data) }?.let { result -> call?.let { it(data) }?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -481,24 +774,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send( rxBus.send(
EventNSClientNewLog( EventNSClientNewLog(
when (operation) { when (operation) {
Operation.CREATE -> "ADD $collection" Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection" Operation.UPDATE -> "UPDATE $collection"
}, },
when (operation) { when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress" Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress" Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
} }
) )
) )
call?.let { it(data) }?.let { result -> call?.let { it(data) }?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -564,24 +857,24 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send( rxBus.send(
EventNSClientNewLog( EventNSClientNewLog(
when (operation) { when (operation) {
Operation.CREATE -> "ADD $collection" Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection" Operation.UPDATE -> "UPDATE $collection"
}, },
when (operation) { when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress" Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress" Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id <i>${gson.toJson(data)}</i> $progress"
} }
) )
) )
call?.let { it(data) }?.let { result -> call?.let { it(data) }?.let { result ->
when (result.response) { when (result.response) {
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}")) 200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}")) 201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}")) 404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
else -> { else -> {
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch return@launch
} }
} }
@ -708,35 +1001,23 @@ class NSClientV3Plugin @Inject constructor(
sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified)) sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified))
} }
fun scheduleIrregularExecution() {
var origin = "5_MIN_AFTER_BG"
var forceNew = true
var toTime = lastLoadedSrvModified.collections.entries + T.mins(5).plus(T.secs(10)).msecs()
if (toTime < dateUtil.now()) {
toTime = dateUtil.now() + T.mins(1).plus(T.secs(0)).msecs()
origin = "1_MIN_OLD_DATA"
forceNew = false
}
handler.postDelayed({ executeLoop(origin, forceNew = forceNew) }, toTime - dateUtil.now())
rxBus.send(EventNSClientNewLog("NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
}
private fun executeLoop(origin: String, forceNew: Boolean) { private fun executeLoop(origin: String, forceNew: Boolean) {
if (sp.getBoolean(R.string.key_ns_client_paused, false)) { if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && initialLoadFinished) return
rxBus.send(EventNSClientNewLog("RUN", "paused")) if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("● RUN", "paused $origin"))
return return
} }
if (!isAllowed) { if (!isAllowed) {
rxBus.send(EventNSClientNewLog("RUN", blockingReason)) rxBus.send(EventNSClientNewLog("● RUN", "$blockingReason $origin"))
return return
} }
if (workIsRunning(arrayOf(JOB_NAME))) { if (workIsRunning(arrayOf(JOB_NAME))) {
rxBus.send(EventNSClientNewLog("RUN", "Already running $origin")) rxBus.send(EventNSClientNewLog("● RUN", "Already running $origin"))
if (!forceNew) return if (!forceNew) return
// Wait for end and start new cycle // Wait for end and start new cycle
while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000) while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000)
} }
rxBus.send(EventNSClientNewLog("RUN", "Starting next round $origin")) rxBus.send(EventNSClientNewLog("RUN", "Starting next round $origin"))
WorkManager.getInstance(context) WorkManager.getInstance(context)
.beginUniqueWork( .beginUniqueWork(
JOB_NAME, JOB_NAME,
@ -754,6 +1035,30 @@ class NSClientV3Plugin @Inject constructor(
.enqueue() .enqueue()
} }
private fun executeUpload(origin: String, forceNew: Boolean) {
if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("● RUN", "paused"))
return
}
if (!isAllowed) {
rxBus.send(EventNSClientNewLog("● RUN", blockingReason))
return
}
if (workIsRunning(arrayOf(JOB_NAME))) {
rxBus.send(EventNSClientNewLog("● RUN", "Already running $origin"))
if (!forceNew) return
// Wait for end and start new cycle
while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000)
}
rxBus.send(EventNSClientNewLog("● RUN", "Starting upload $origin"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
JOB_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.Builder(DataSyncWorker::class.java).build()
)
}
private fun workIsRunning(workNames: Array<String>): Boolean { private fun workIsRunning(workNames: Array<String>): Boolean {
for (workName in workNames) for (workName in workNames)
for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get()) for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get())
@ -761,20 +1066,4 @@ class NSClientV3Plugin @Inject constructor(
return true return true
return false return false
} }
private val eventWorker = Executors.newSingleThreadScheduledExecutor()
private var scheduledEventPost: ScheduledFuture<*>? = null
private fun delayAndScheduleExecution(origin: String) {
class PostRunnable : Runnable {
override fun run() {
scheduledEventPost = null
executeLoop(origin, forceNew = true)
}
}
// cancel waiting task to prevent sending multiple posts
scheduledEventPost?.cancel(false)
val task: Runnable = PostRunnable()
scheduledEventPost = eventWorker.schedule(task, 10, TimeUnit.SECONDS)
}
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -48,7 +48,7 @@
<!-- NSClient --> <!-- NSClient -->
<string name="key_ns_client_autoscroll" translatable="false">ns_client_autoscroll</string> <string name="key_ns_client_autoscroll" translatable="false">ns_client_autoscroll</string>
<string name="key_ns_client_paused" translatable="false">ns_client_paused</string> <string name="key_ns_paused" translatable="false">ns_client_paused</string>
<string name="key_ns_log_app_started_event" translatable="false">ns_log_app_started_event</string> <string name="key_ns_log_app_started_event" translatable="false">ns_log_app_started_event</string>
<string name="no_write_permission">NSCLIENT has no write permission. Wrong API secret?</string> <string name="no_write_permission">NSCLIENT has no write permission. Wrong API secret?</string>
@ -171,5 +171,7 @@
<!-- DataBroadcast--> <!-- DataBroadcast-->
<string name="data_broadcaster" translatable="false">Data Broadcaster</string> <string name="data_broadcaster" translatable="false">Data Broadcaster</string>
<string name="ns_use_ws_title">Connect to websockets</string>
<string name="ns_use_ws_summary">Enabling means: faster updates, receiving alarms and announcements and higher battery consumption similar to v1. All other uploaderds to NS must use v3 protocol.</string>
</resources> </resources>

View file

@ -9,7 +9,7 @@
app:initialExpandedChildrenCount="0"> app:initialExpandedChildrenCount="0">
<info.nightscout.core.validators.ValidatingEditTextPreference <info.nightscout.core.validators.ValidatingEditTextPreference
android:defaultValue="https://{YOUR-SITE}.azurewebsites.net/" android:defaultValue="https://{YOUR-SITE}.azurewebsites.net"
android:dialogMessage="@string/ns_client_url_dialog_message" android:dialogMessage="@string/ns_client_url_dialog_message"
android:inputType="textUri" android:inputType="textUri"
android:key="@string/key_nsclientinternal_url" android:key="@string/key_nsclientinternal_url"
@ -24,7 +24,13 @@
android:key="@string/key_ns_client_token" android:key="@string/key_ns_client_token"
android:title="@string/nsclient_token_title" android:title="@string/nsclient_token_title"
validate:minLength="17" validate:minLength="17"
validate:testType="minLength"/> validate:testType="minLength" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_ns_use_ws"
android:summary="@string/ns_use_ws_summary"
android:title="@string/ns_use_ws_title" />
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="@string/ns_sync_options" android:key="@string/ns_sync_options"