diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/DeviceStatusMapper.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/DeviceStatusMapper.kt index e2ca241a48..2b4334183f 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/DeviceStatusMapper.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/DeviceStatusMapper.kt @@ -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, diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/FoodMapper.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/FoodMapper.kt index 15bb3978c9..49ebe6456c 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/FoodMapper.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/FoodMapper.kt @@ -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" -> diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/SvgMapper.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/SvgMapper.kt index b37a600a86..bc08e4e6f2 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/SvgMapper.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/SvgMapper.kt @@ -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 diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/TreatmentMapper.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/TreatmentMapper.kt index 2dbd17f74b..64a500aa81 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/TreatmentMapper.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/mapper/TreatmentMapper.kt @@ -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 { diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/LastModified.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/LastModified.kt index 2a9716adc6..4d48a5f46d 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/LastModified.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/LastModified.kt @@ -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 + } + } } diff --git a/core/utils/src/main/res/values/keys.xml b/core/utils/src/main/res/values/keys.xml index 8377c4b5d7..2ea8eb2c02 100644 --- a/core/utils/src/main/res/values/keys.xml +++ b/core/utils/src/main/res/values/keys.xml @@ -85,6 +85,7 @@ ns_receive_profile_store nsclientinternal_url nsclientinternal_api_secret + ns_use_ws ns_receive_insulin ns_receive_carbs ns_receive_therapy_events diff --git a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt index 81643a8d2d..1696e5c6a0 100644 --- a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt +++ b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt @@ -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) } diff --git a/plugins/source/src/main/java/info/nightscout/source/NSClientSourcePlugin.kt b/plugins/source/src/main/java/info/nightscout/source/NSClientSourcePlugin.kt index 9863eff071..b5ccfe6387 100644 --- a/plugins/source/src/main/java/info/nightscout/source/NSClientSourcePlugin.kt +++ b/plugins/source/src/main/java/info/nightscout/source/NSClientSourcePlugin.kt @@ -116,7 +116,7 @@ class NSClientSourcePlugin @Inject constructor( return TransactionGlucoseValue( timestamp = sgv.date ?: throw InvalidParameterException(), value = sgv.sgv, - noise = sgv.noise?.toDouble(), + noise = sgv.noise, raw = sgv.filtered, trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction?.nsName), nightscoutId = sgv.identifier, diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/di/SyncModule.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/di/SyncModule.kt index 731d4b729a..f1ef1bece4 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/di/SyncModule.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/di/SyncModule.kt @@ -60,6 +60,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 diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/DataSyncSelectorImplementation.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/DataSyncSelectorImplementation.kt index 60562c323c..91ec86fca5 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/DataSyncSelectorImplementation.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/DataSyncSelectorImplementation.kt @@ -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() diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/NSClientFragment.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/NSClientFragment.kt index b3736b070e..ecd92f2270 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/NSClientFragment.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/NSClientFragment.kt @@ -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 diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/StoreDataForDbImpl.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/StoreDataForDbImpl.kt index 445c934835..e47281cd5e 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/StoreDataForDbImpl.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/StoreDataForDbImpl.kt @@ -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 HashMap.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 { @@ -184,12 +195,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 { @@ -214,12 +223,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 { @@ -796,7 +803,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() @@ -996,7 +1003,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) { diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/events/EventNSConnectivityOptionChanged.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/events/EventNSConnectivityOptionChanged.kt new file mode 100644 index 0000000000..e5a50ba7d5 --- /dev/null +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsShared/events/EventNSConnectivityOptionChanged.kt @@ -0,0 +1,5 @@ +package info.nightscout.plugins.sync.nsShared.events + +import info.nightscout.rx.events.Event + +class EventNSConnectivityOptionChanged(val blockingReason: String) : Event() \ No newline at end of file diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NSClientPlugin.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NSClientPlugin.kt index 53ca1d3e73..7adc85cf6a 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NSClientPlugin.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NSClientPlugin.kt @@ -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 @@ -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 ?: "" diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NsClientReceiverDelegate.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NsClientReceiverDelegate.kt index b515ee08ab..bae5c3fe4b 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NsClientReceiverDelegate.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/NsClientReceiverDelegate.kt @@ -1,14 +1,19 @@ 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.EventNSConnectivityOptionChanged +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 @@ -18,26 +23,45 @@ class NsClientReceiverDelegate @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(EventNSConnectivityOptionChanged(blockingReason)) } } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/services/NSClientService.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/services/NSClientService.kt index 24aeeaec0b..affb7e1ab2 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/services/NSClientService.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclient/services/NSClientService.kt @@ -33,6 +33,7 @@ import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus 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.acks.NSAddAck import info.nightscout.plugins.sync.nsclient.acks.NSAuthAck @@ -140,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(EventNSConnectivityOptionChanged::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + latestDateInReceivedData = 0 + destroy() + initialize() + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventAppExit::class.java) .observeOn(aapsSchedulers.io) @@ -218,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) { @@ -230,9 +239,7 @@ 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) diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt index 894c2ad86a..b80cb08de1 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt @@ -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,34 @@ 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.utils.JsonHelper +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.EventNSClientResend 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.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 +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.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 +76,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 +87,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 @@ -102,7 +120,12 @@ class NSClientV3Plugin @Inject constructor( 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) @@ -118,7 +141,6 @@ class NSClientV3Plugin @Inject constructor( companion object { val JOB_NAME: String = this::class.java.simpleName - val REFRESH_INTERVAL = T.secs(30).msecs() const val RECORDS_TO_LOAD = 500L } @@ -130,14 +152,16 @@ 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 @@ -161,26 +185,30 @@ class NSClientV3Plugin @Inject constructor( ) ) - setClient() + setClient("START") nsClientReceiverDelegate.grabReceiversState() disposable += rxBus - .toObservable(EventNetworkChange::class.java) + .toObservable(EventNSConnectivityOptionChanged::class.java) .observeOn(aapsSchedulers.io) .subscribe({ ev -> - nsClientReceiverDelegate.onStatusEvent(ev) - setClient() - rxBus.send(EventNSClientUpdateGUI()) + rxBus.send(EventNSClientNewLog("● CONNECTIVITY CHANGE", ev.blockingReason)) + setClient("CONNECTIVITY_CHANGE") + if (isAllowed) executeLoop("CONNECTIVITY_CHANGE", 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)) + ) + setClient("SETTING 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) disposable += rxBus .toObservable(EventAppExit::class.java) @@ -193,47 +221,55 @@ 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() { - // context.applicationContext.unbindService(mConnection) handler.removeCallbacksAndMessages(null) disposable.clear() + socket?.disconnect() super.onStop() } @@ -257,16 +293,269 @@ 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, ""), context = context, logging = true ) + if (wsConnected) + socket?.disconnect() + SystemClock.sleep(2000) + initializeWebSockets(reason) 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("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 $docString")) + 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 -> + 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) { synchronized(listLog) { listLog.add(ev) @@ -292,12 +581,15 @@ 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 @@ -337,6 +629,7 @@ class NSClientV3Plugin @Inject constructor( override fun resetToFullSync() { firstLoadContinueTimestamp = LastModified(LastModified.Collections()) lastLoadedSrvModified = LastModified(LastModified.Collections()) + initialLoadFinished = false storeLastLoadedSrvModified() } @@ -355,15 +648,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} $data $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 } } @@ -385,15 +678,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} ${gson.toJson(data)} $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 } } @@ -426,24 +719,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} ${gson.toJson(data)} $progress" + Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $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 } } @@ -481,24 +774,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} ${gson.toJson(data)} $progress" + Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $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 } } @@ -564,24 +857,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} ${gson.toJson(data)} $progress" + Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $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 } } @@ -708,35 +1001,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, @@ -754,6 +1035,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): Boolean { for (workName in workNames) for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get()) @@ -761,20 +1066,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) - } } \ No newline at end of file diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/DataSyncWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/DataSyncWorker.kt index 0c446063b6..69b1d5204f 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/DataSyncWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/DataSyncWorker.kt @@ -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() diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt index 93afbb4d5f..3c1451eee8 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt @@ -70,7 +70,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 @@ -90,7 +90,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, @@ -106,7 +106,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, @@ -118,7 +118,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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt index 437f2acae2..4a3acdfce1 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt @@ -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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt index 8a92a1f703..74b3b5ff25 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt @@ -40,7 +40,7 @@ class LoadFoodsWorker( if (nsClientV3Plugin.lastLoadedSrvModified.collections.foods++ % 5 == 0L) { val foods: List = 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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt index c4333ab28b..addcf27ce6 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt @@ -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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt index 0acae4ffc4..84b3cf245d 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt @@ -57,7 +57,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, @@ -68,7 +68,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, @@ -77,7 +77,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, @@ -87,7 +87,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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt index d51a286306..806e743f1d 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt @@ -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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt index b5159fa9bf..7e91ed9281 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt @@ -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 @@ -57,14 +58,14 @@ 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 WorkManager.getInstance(context) .beginUniqueWork( 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 @@ -77,14 +78,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 @@ -92,18 +94,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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessFoodWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessFoodWorker.kt index 32bdc3ac54..b6254b7931 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessFoodWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessFoodWorker.kt @@ -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)) } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessTreatmentsWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessTreatmentsWorker.kt index 3f96ad4922..fb1e1e3cde 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessTreatmentsWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/ProcessTreatmentsWorker.kt @@ -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>? + val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as List? ?: 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() diff --git a/plugins/sync/src/main/res/values/strings.xml b/plugins/sync/src/main/res/values/strings.xml index ce19f868b7..44263daa2d 100644 --- a/plugins/sync/src/main/res/values/strings.xml +++ b/plugins/sync/src/main/res/values/strings.xml @@ -48,7 +48,7 @@ ns_client_autoscroll - ns_client_paused + ns_client_paused ns_log_app_started_event NSCLIENT has no write permission. Wrong API secret? @@ -171,5 +171,7 @@ Data Broadcaster + Connect to websockets + Enabling means: faster updates, receiving alarms and announcements and higher battery consumption similar to v1. All other uploaderds to NS must use v3 protocol. \ No newline at end of file diff --git a/plugins/sync/src/main/res/xml/pref_ns_client_v3.xml b/plugins/sync/src/main/res/xml/pref_ns_client_v3.xml index 79f5b522af..974a2b89d9 100644 --- a/plugins/sync/src/main/res/xml/pref_ns_client_v3.xml +++ b/plugins/sync/src/main/res/xml/pref_ns_client_v3.xml @@ -9,7 +9,7 @@ app:initialExpandedChildrenCount="0"> + validate:testType="minLength" /> + +