diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/utils/HtmlHelper.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/utils/HtmlHelper.kt index 753be44cc0..004010e0bf 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/utils/HtmlHelper.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/utils/HtmlHelper.kt @@ -1,10 +1,15 @@ package info.nightscout.interfaces.utils import android.text.Html +import android.text.SpannableStringBuilder import android.text.Spanned object HtmlHelper { - fun fromHtml(source: String): Spanned { - return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY) - } + + fun fromHtml(source: String): Spanned = + try { + Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY) + } catch (e: Exception) { + SpannableStringBuilder("") + } } \ No newline at end of file diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt index 0af094f4d3..961d7429a8 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt @@ -227,6 +227,34 @@ class NSAndroidClientImpl( } } + override suspend fun createDeviceStatus(remoteDeviceStatus: RemoteDeviceStatus): CreateUpdateResponse = callWrapper(dispatcher) { + + remoteDeviceStatus.app = "AAPS" + val response = api.createDeviceStatus(remoteDeviceStatus) + if (response.isSuccessful) { + if (response.code() == 200) { + return@callWrapper CreateUpdateResponse( + response = response.code(), + identifier = null, + isDeduplication = true, + deduplicatedIdentifier = null, + lastModified = null + ) + } else if (response.code() == 201) { + return@callWrapper CreateUpdateResponse( + response = response.code(), + identifier = response.body()?.result?.identifier + ?: throw UnknownResponseNightscoutException(), + isDeduplication = response.body()?.result?.isDeduplication ?: false, + deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier, + lastModified = response.body()?.result?.lastModified + ) + } else throw UnknownResponseNightscoutException() + } else { + throw UnsuccessfullNightscoutException() + } + } + override suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse = callWrapper(dispatcher) { val remoteTreatment = nsTreatment.toRemoteTreatment() ?: throw InvalidFormatNightscoutException() diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidRxClientImpl.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidRxClientImpl.kt index 18c54fea29..617af7d803 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidRxClientImpl.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidRxClientImpl.kt @@ -3,22 +3,13 @@ package info.nightscout.sdk import info.nightscout.sdk.interfaces.NSAndroidClient import info.nightscout.sdk.interfaces.NSAndroidRxClient import info.nightscout.sdk.localmodel.Status -import info.nightscout.sdk.localmodel.entry.NSSgvV3 -import info.nightscout.sdk.localmodel.treatment.NSTreatment import info.nightscout.sdk.remotemodel.LastModified -import info.nightscout.sdk.remotemodel.RemoteDeviceStatus import io.reactivex.rxjava3.core.Single import kotlinx.coroutines.rx3.rxSingle -import retrofit2.http.Query class NSAndroidRxClientImpl(private val client: NSAndroidClient) : NSAndroidRxClient { override fun getVersion(): Single = rxSingle { client.getVersion() } override fun getStatus(): Single = rxSingle { client.getStatus() } override fun getLastModified(): Single = rxSingle { client.getLastModified() } - override fun getSgvsModifiedSince(from: Long, limit: Long): Single>> = rxSingle { client.getSgvsModifiedSince(from, limit) } - override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single>> = - rxSingle { client.getTreatmentsModifiedSince(from, limit) } - override fun getDeviceStatusModifiedSince(from: Long): Single> = - rxSingle { client.getDeviceStatusModifiedSince(from) } } diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt index bc481744ab..eedc970bdf 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt @@ -19,17 +19,23 @@ interface NSAndroidClient { suspend fun getVersion(): String suspend fun getStatus(): Status suspend fun getLastModified(): LastModified + suspend fun getSgvs(): List suspend fun getSgvsModifiedSince(from: Long, limit: Long): ReadResponse> suspend fun getSgvsNewerThan(from: Long, limit: Long): List suspend fun createSvg(nsSgvV3: NSSgvV3): CreateUpdateResponse suspend fun updateSvg(nsSgvV3: NSSgvV3): CreateUpdateResponse + suspend fun getTreatmentsNewerThan(createdAt: String, limit: Long): List suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): ReadResponse> + + suspend fun createDeviceStatus(remoteDeviceStatus: RemoteDeviceStatus): CreateUpdateResponse suspend fun getDeviceStatusModifiedSince(from: Long): List + suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse suspend fun updateTreatment(nsTreatment: NSTreatment): CreateUpdateResponse suspend fun getFoods(limit: Long): List + //suspend fun getFoodsModifiedSince(from: Long, limit: Long): ReadResponse> suspend fun createFood(nsFood: NSFood): CreateUpdateResponse suspend fun updateFood(nsFood: NSFood): CreateUpdateResponse diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidRxClient.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidRxClient.kt index f1e6c2df1e..503fa9f09d 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidRxClient.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidRxClient.kt @@ -1,10 +1,7 @@ package info.nightscout.sdk.interfaces import info.nightscout.sdk.localmodel.Status -import info.nightscout.sdk.localmodel.entry.NSSgvV3 -import info.nightscout.sdk.localmodel.treatment.NSTreatment import info.nightscout.sdk.remotemodel.LastModified -import info.nightscout.sdk.remotemodel.RemoteDeviceStatus import io.reactivex.rxjava3.core.Single interface NSAndroidRxClient { @@ -12,8 +9,5 @@ interface NSAndroidRxClient { fun getVersion(): Single fun getStatus(): Single fun getLastModified(): Single - fun getSgvsModifiedSince(from: Long, limit: Long): Single>> - fun getTreatmentsModifiedSince(from: Long, limit: Long): Single>> - fun getDeviceStatusModifiedSince(from: Long): Single> } diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt index 9278b98a6a..824aada95e 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt @@ -56,15 +56,18 @@ internal interface NightscoutRemoteService { @GET("v3/treatments/history/{from}") suspend fun getTreatmentsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response>> - @GET("v3/devicestatus/history/{from}") - suspend fun getDeviceStatusModifiedSince(@Path("from") from: Long): Response>> - @POST("v3/treatments") suspend fun createTreatment(@Body remoteTreatment: RemoteTreatment): Response> @PATCH("v3/treatments/{identifier}") suspend fun updateTreatment(@Body remoteTreatment: RemoteTreatment, @Path("identifier") identifier: String): Response> + @POST("v3/devicestatus") + suspend fun createDeviceStatus(@Body remoteDeviceStatus: RemoteDeviceStatus): Response> + + @GET("v3/devicestatus/history/{from}") + suspend fun getDeviceStatusModifiedSince(@Path("from") from: Long): Response>> + @GET("v3/food") suspend fun getFoods(@Query("limit") limit: Long): Response>> /* diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt index 70558483b1..1a0c5b155e 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt @@ -11,10 +11,12 @@ import org.json.JSONObject **/ @Serializable data class RemoteDeviceStatus( - @SerializedName("identifier") val identifier: String?, // string Main addressing, required field that identifies document in the collection. The client should not create the identifier, the server automatically assigns it when the document is inserted. - @SerializedName("srvCreated") val srvCreated: Long?, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3. - @SerializedName("srvModified") val srvModified: Long?, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted). - @SerializedName("created_at") val createdAt: String?, // string or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long... + @SerializedName("app") var app: String? = null, + @SerializedName("identifier") val identifier: String? = null, // string Main addressing, required field that identifies document in the collection. The client should not create the identifier, the server automatically assigns it when the document is inserted. + @SerializedName("srvCreated") val srvCreated: Long? = null, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3. + @SerializedName("srvModified") val srvModified: Long? = null, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted). + @SerializedName("created_at") val createdAt: String? = null, // string or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long... + @SerializedName("date") val date: Long?, // date as milliseconds @SerializedName("uploaderBattery") val uploaderBattery: Int?,// integer($int64) @SerializedName("device") val device: String?, // "openaps://samsung SM-G970F" diff --git a/plugins/sync/build.gradle b/plugins/sync/build.gradle index 0d225d45d9..f1253e74ea 100644 --- a/plugins/sync/build.gradle +++ b/plugins/sync/build.gradle @@ -28,6 +28,7 @@ dependencies { testImplementation "androidx.work:work-testing:$work_version" testImplementation project(':implementation') + testImplementation project(':plugins:aps') // NSClient, Tidepool api("io.socket:socket.io-client:1.0.2") { 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 6d34fb6226..1b227f0705 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 @@ -47,6 +47,7 @@ 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.extensions.toRemoteDeviceStatus import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker @@ -330,281 +331,321 @@ class NSClientV3Plugin @Inject constructor( enum class Operation { CREATE, UPDATE } private val gson: Gson = GsonBuilder().create() + private fun dbOperationDeviceStatus(collection: String = "devicestatus", dataPair: DataSyncSelector.DataPair, progress: String) { + val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toRemoteDeviceStatus() + scope.launch { + try { + 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}")) + + else -> { + rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) + return@launch + } + } + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdDeviceStatuses.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastDeviceStatusIdIfGreater(dataPair.id) + dataSyncSelector.processChangedDeviceStatusesCompat() + } + } catch (e: Exception) { + aapsLogger.error(LTag.NSCLIENT, "Upload exception", e) + } + } + } + + private fun dbOperationEntries(collection: String = "entries", dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) { + val call = when (operation) { + Operation.CREATE -> nsAndroidClient?.let { return@let it::createSvg } + Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateSvg } + } + when (dataPair) { + is DataSyncSelector.PairGlucoseValue -> dataPair.value.toNSSvgV3() + else -> null + }?.let { data -> + scope.launch { + try { + val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else "" + rxBus.send( + EventNSClientNewLog( + when (operation) { + 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" + } + ) + ) + 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}")) + + else -> { + rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) + return@launch + } + } + when (dataPair) { + is DataSyncSelector.PairGlucoseValue -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdGlucoseValues.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastGlucoseValueIdIfGreater(dataPair.id) + dataSyncSelector.processChangedGlucoseValuesCompat() + } + } + } + } catch (e: Exception) { + aapsLogger.error(LTag.NSCLIENT, "Upload exception", e) + } + } + } + } + + private fun dbOperationFood(collection: String = "food", dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) { + val call = when (operation) { + Operation.CREATE -> nsAndroidClient?.let { return@let it::createFood } + Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateFood } + } + when (dataPair) { + is DataSyncSelector.PairFood -> dataPair.value.toNSFood() + else -> null + }?.let { data -> + scope.launch { + try { + val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else "" + rxBus.send( + EventNSClientNewLog( + when (operation) { + 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" + } + ) + ) + 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}")) + + else -> { + rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) + return@launch + } + } + when (dataPair) { + is DataSyncSelector.PairFood -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdFoods.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastFoodIdIfGreater(dataPair.id) + dataSyncSelector.processChangedFoodsCompat() + } + } + } + } catch (e: Exception) { + aapsLogger.error(LTag.NSCLIENT, "Upload exception", e) + } + } + } + } + + private fun dbOperationTreatments(collection: String = "treatments", dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) { + val call = when (operation) { + Operation.CREATE -> nsAndroidClient?.let { return@let it::createTreatment } + Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateTreatment } + } + when (dataPair) { + is DataSyncSelector.PairBolus -> dataPair.value.toNSBolus() + is DataSyncSelector.PairCarbs -> dataPair.value.toNSCarbs() + is DataSyncSelector.PairBolusCalculatorResult -> dataPair.value.toNSBolusWizard() + is DataSyncSelector.PairTemporaryTarget -> dataPair.value.toNSTemporaryTarget() + is DataSyncSelector.PairTherapyEvent -> dataPair.value.toNSTherapyEvent() + + is DataSyncSelector.PairTemporaryBasal -> { + val profile = profileFunction.getProfile(dataPair.value.timestamp) + if (profile == null) { + dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(dataPair.id) + dataSyncSelector.processChangedTemporaryBasalsCompat() + return + } + dataPair.value.toNSTemporaryBasal(profile) + } + + is DataSyncSelector.PairExtendedBolus -> { + val profile = profileFunction.getProfile(dataPair.value.timestamp) + if (profile == null) { + dataSyncSelector.confirmLastExtendedBolusIdIfGreater(dataPair.id) + dataSyncSelector.processChangedExtendedBolusesCompat() + return + } + dataPair.value.toNSExtendedBolus(profile) + } + + is DataSyncSelector.PairProfileSwitch -> dataPair.value.toNSProfileSwitch(dateUtil) + is DataSyncSelector.PairEffectiveProfileSwitch -> dataPair.value.toNSEffectiveProfileSwitch(dateUtil) + is DataSyncSelector.PairOfflineEvent -> dataPair.value.toNSOfflineEvent() + else -> null + }?.let { data -> + scope.launch { + try { + val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else "" + rxBus.send( + EventNSClientNewLog( + when (operation) { + 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" + } + ) + ) + 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}")) + + else -> { + rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) + return@launch + } + } + when (dataPair) { + is DataSyncSelector.PairBolus -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdBoluses.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastBolusIdIfGreater(dataPair.id) + dataSyncSelector.processChangedBolusesCompat() + } + + is DataSyncSelector.PairCarbs -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdCarbs.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastCarbsIdIfGreater(dataPair.id) + dataSyncSelector.processChangedCarbsCompat() + } + + is DataSyncSelector.PairBolusCalculatorResult -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdBolusCalculatorResults.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(dataPair.id) + dataSyncSelector.processChangedBolusCalculatorResultsCompat() + } + + is DataSyncSelector.PairTemporaryTarget -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdTemporaryTargets.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastTempTargetsIdIfGreater(dataPair.id) + dataSyncSelector.processChangedTempTargetsCompat() + } + + is DataSyncSelector.PairTherapyEvent -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdTherapyEvents.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastTherapyEventIdIfGreater(dataPair.id) + dataSyncSelector.processChangedTherapyEventsCompat() + } + + is DataSyncSelector.PairTemporaryBasal -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdTemporaryBasals.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(dataPair.id) + dataSyncSelector.processChangedTemporaryBasalsCompat() + } + + is DataSyncSelector.PairExtendedBolus -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdExtendedBoluses.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastExtendedBolusIdIfGreater(dataPair.id) + dataSyncSelector.processChangedExtendedBolusesCompat() + } + + is DataSyncSelector.PairProfileSwitch -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdProfileSwitches.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastProfileSwitchIdIfGreater(dataPair.id) + dataSyncSelector.processChangedProfileSwitchesCompat() + } + + is DataSyncSelector.PairEffectiveProfileSwitch -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdEffectiveProfileSwitches.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(dataPair.id) + dataSyncSelector.processChangedEffectiveProfileSwitchesCompat() + } + + is DataSyncSelector.PairOfflineEvent -> { + if (result.response == 201) { // created + dataPair.value.interfaceIDs.nightscoutId = result.identifier + storeDataForDb.nsIdOfflineEvents.add(dataPair.value) + storeDataForDb.scheduleNsIdUpdate() + } + dataSyncSelector.confirmLastOfflineEventIdIfGreater(dataPair.id) + dataSyncSelector.processChangedOfflineEventsCompat() + } + } + } + } catch (e: Exception) { + aapsLogger.error(LTag.NSCLIENT, "Upload exception", e) + } + } + } + } + private fun dbOperation(collection: String, dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) { - if (collection == "entries") { - val call = when (operation) { - Operation.CREATE -> nsAndroidClient?.let { return@let it::createSvg } - Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateSvg } - } - when (dataPair) { - is DataSyncSelector.PairGlucoseValue -> dataPair.value.toNSSvgV3() - else -> null - }?.let { data -> - scope.launch { - try { - val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else "" - rxBus.send( - EventNSClientNewLog( - when (operation) { - 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" - } - ) - ) - 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}")) - - else -> { - rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) - return@launch - } - } - when (dataPair) { - is DataSyncSelector.PairGlucoseValue -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdGlucoseValues.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastGlucoseValueIdIfGreater(dataPair.id) - dataSyncSelector.processChangedGlucoseValuesCompat() - } - } - } - } catch (e: Exception) { - aapsLogger.error(LTag.NSCLIENT, "Upload exception", e) - } - } - } - } - if (collection == "food") { - val call = when (operation) { - Operation.CREATE -> nsAndroidClient?.let { return@let it::createFood } - Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateFood } - } - when (dataPair) { - is DataSyncSelector.PairFood -> dataPair.value.toNSFood() - else -> null - }?.let { data -> - scope.launch { - try { - val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else "" - rxBus.send( - EventNSClientNewLog( - when (operation) { - 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" - } - ) - ) - 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}")) - - else -> { - rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) - return@launch - } - } - when (dataPair) { - is DataSyncSelector.PairFood -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdFoods.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastFoodIdIfGreater(dataPair.id) - dataSyncSelector.processChangedFoodsCompat() - } - } - } - } catch (e: Exception) { - aapsLogger.error(LTag.NSCLIENT, "Upload exception", e) - } - } - } - } - if (collection == "treatments") { - val call = when (operation) { - Operation.CREATE -> nsAndroidClient?.let { return@let it::createTreatment } - Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateTreatment } - } - when (dataPair) { - is DataSyncSelector.PairBolus -> dataPair.value.toNSBolus() - is DataSyncSelector.PairCarbs -> dataPair.value.toNSCarbs() - is DataSyncSelector.PairBolusCalculatorResult -> dataPair.value.toNSBolusWizard() - is DataSyncSelector.PairTemporaryTarget -> dataPair.value.toNSTemporaryTarget() - is DataSyncSelector.PairTherapyEvent -> dataPair.value.toNSTherapyEvent() - - is DataSyncSelector.PairTemporaryBasal -> { - val profile = profileFunction.getProfile(dataPair.value.timestamp) - if (profile == null) { - dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(dataPair.id) - dataSyncSelector.processChangedTemporaryBasalsCompat() - return - } - dataPair.value.toNSTemporaryBasal(profile) - } - is DataSyncSelector.PairExtendedBolus -> { - val profile = profileFunction.getProfile(dataPair.value.timestamp) - if (profile == null) { - dataSyncSelector.confirmLastExtendedBolusIdIfGreater(dataPair.id) - dataSyncSelector.processChangedExtendedBolusesCompat() - return - } - dataPair.value.toNSExtendedBolus(profile) - } - is DataSyncSelector.PairProfileSwitch -> dataPair.value.toNSProfileSwitch(dateUtil) - is DataSyncSelector.PairEffectiveProfileSwitch -> dataPair.value.toNSEffectiveProfileSwitch(dateUtil) - is DataSyncSelector.PairOfflineEvent -> dataPair.value.toNSOfflineEvent() - else -> null - }?.let { data -> - scope.launch { - try { - val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else "" - rxBus.send( - EventNSClientNewLog( - when (operation) { - 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" - } - ) - ) - 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}")) - - else -> { - rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} ")) - return@launch - } - } - when (dataPair) { - is DataSyncSelector.PairBolus -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdBoluses.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastBolusIdIfGreater(dataPair.id) - dataSyncSelector.processChangedBolusesCompat() - } - - is DataSyncSelector.PairCarbs -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdCarbs.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastCarbsIdIfGreater(dataPair.id) - dataSyncSelector.processChangedCarbsCompat() - } - - is DataSyncSelector.PairBolusCalculatorResult -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdBolusCalculatorResults.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(dataPair.id) - dataSyncSelector.processChangedBolusCalculatorResultsCompat() - } - - is DataSyncSelector.PairTemporaryTarget -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdTemporaryTargets.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastTempTargetsIdIfGreater(dataPair.id) - dataSyncSelector.processChangedTempTargetsCompat() - } - - is DataSyncSelector.PairTherapyEvent -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdTherapyEvents.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastTherapyEventIdIfGreater(dataPair.id) - dataSyncSelector.processChangedTherapyEventsCompat() - } - - is DataSyncSelector.PairTemporaryBasal -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdTemporaryBasals.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(dataPair.id) - dataSyncSelector.processChangedTemporaryBasalsCompat() - } - - is DataSyncSelector.PairExtendedBolus -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdExtendedBoluses.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastExtendedBolusIdIfGreater(dataPair.id) - dataSyncSelector.processChangedExtendedBolusesCompat() - } - - is DataSyncSelector.PairProfileSwitch -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdProfileSwitches.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastProfileSwitchIdIfGreater(dataPair.id) - dataSyncSelector.processChangedProfileSwitchesCompat() - } - - is DataSyncSelector.PairEffectiveProfileSwitch -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdEffectiveProfileSwitches.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(dataPair.id) - dataSyncSelector.processChangedEffectiveProfileSwitchesCompat() - } - - is DataSyncSelector.PairOfflineEvent -> { - if (result.response == 201) { // created - dataPair.value.interfaceIDs.nightscoutId = result.identifier - storeDataForDb.nsIdOfflineEvents.add(dataPair.value) - storeDataForDb.scheduleNsIdUpdate() - } - dataSyncSelector.confirmLastOfflineEventIdIfGreater(dataPair.id) - dataSyncSelector.processChangedOfflineEventsCompat() - } - } - } - } catch (e: Exception) { - aapsLogger.error(LTag.NSCLIENT, "Upload exception", e) - } - } - } + when (collection) { + "devicestatus" -> dbOperationDeviceStatus(dataPair = dataPair, progress = progress) + "entries" -> dbOperationEntries(dataPair = dataPair, progress = progress, operation = operation) + "food" -> dbOperationFood(dataPair = dataPair, progress = progress, operation = operation) + "treatments" -> dbOperationTreatments(dataPair = dataPair, progress = progress, operation = operation) } } diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/extensions/DeviceStatusExtension.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/extensions/DeviceStatusExtension.kt new file mode 100644 index 0000000000..358e340931 --- /dev/null +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/extensions/DeviceStatusExtension.kt @@ -0,0 +1,389 @@ +package info.nightscout.plugins.sync.nsclientV3.extensions + +import com.google.gson.GsonBuilder +import com.google.gson.JsonDeserializer +import info.nightscout.database.entities.DeviceStatus +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus +import org.json.JSONObject + +fun DeviceStatus.toRemoteDeviceStatus(): RemoteDeviceStatus { + val deserializer: JsonDeserializer = + JsonDeserializer { json, _, _ -> + JSONObject(json.asJsonObject.toString()) + } + val gson = GsonBuilder().also { + it.registerTypeAdapter(JSONObject::class.java, deserializer) + }.create() + + val pump = gson.fromJson(pump, RemoteDeviceStatus.Pump::class.java) + val openAps = RemoteDeviceStatus.OpenAps( + suggested = suggested?.let { JSONObject(it) }, + enacted = enacted?.let { JSONObject(it) }, + iob = iob?.let { JSONObject(it) }, + ) + return RemoteDeviceStatus( + date = timestamp, + device = device, + pump = pump, + openaps = openAps, + uploaderBattery = if (uploaderBattery != 0) uploaderBattery else null, + configuration = gson.fromJson(configuration, RemoteDeviceStatus.Configuration::class.java), + uploader = null + ) +} +/* +{ + "_id": "576cfd15217b0bed77d63641", + "device": "openaps://indy2", + "pump": { + "battery": { + "status": "normal", + "voltage": 1.56 + }, + "status": { + "status": "normal", + "timestamp": "2016-06-24T09:26:38.000Z", + "bolusing": false, + "suspended": false + }, + "reservoir": 31.25, + "clock": "2016-06-24T02:26:16-07:00" + }, + "openaps": { + "suggested": { + "bg": 173, + "temp": "absolute", + "snoozeBG": 194, + "timestamp": "2016-06-24T09:27:40.000Z", + "predBGs": { + "IOB": [173, 178, 183, 187, 191, 194, 196, 197, 198, 197, 197, 195, 192, 190, 187, 184, 181, 178, 175, 172, 169, 167, 164, 162, 160, 158, 156, 154, 152, 151, 149, 148, 147, 146, 146, 145] + }, + "reason": "COB: 0, Dev: 46, BGI: -1.92, ISF: 80, Target: 115; Eventual BG 194>=115, adj. req. rate:2.7 to maxSafeBasal:2.3, temp 2.25 >~ req 2.3U/hr", + "COB": 0, + "eventualBG": 194, + "tick": "+6", + "IOB": 0.309 + }, + "iob": [{ + "netbasalinsulin": -0.3, + "activity": 0.0048, + "basaliob": 0.078, + "time": "2016-06-24T09:26:16.000Z", + "hightempinsulin": 0.25, + "bolussnooze": 0, + "iob": 0.309 + }, { + "netbasalinsulin": -0.15, + "activity": 0.0041, + "basaliob": 0.238, + "time": "2016-06-24T09:31:16.000Z", + "hightempinsulin": 0.4, + "bolussnooze": 0, + "iob": 0.438 + }, { + "netbasalinsulin": 0, + "activity": 0.0036, + "basaliob": 0.345, + "time": "2016-06-24T09:36:16.000Z", + "hightempinsulin": 0.5, + "bolussnooze": 0, + "iob": 0.52 + }, { + "netbasalinsulin": 0.2, + "activity": 0.0036, + "basaliob": 0.5, + "time": "2016-06-24T09:41:16.000Z", + "hightempinsulin": 0.65, + "bolussnooze": 0, + "iob": 0.653 + }, { + "netbasalinsulin": 0.35, + "activity": 0.0038, + "basaliob": 0.602, + "time": "2016-06-24T09:46:16.000Z", + "hightempinsulin": 0.75, + "bolussnooze": 0, + "iob": 0.734 + }, { + "netbasalinsulin": 0.45, + "activity": 0.0042, + "basaliob": 0.651, + "time": "2016-06-24T09:51:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.763 + }, { + "netbasalinsulin": 0.45, + "activity": 0.0045, + "basaliob": 0.647, + "time": "2016-06-24T09:56:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.74 + }, { + "netbasalinsulin": 0.5, + "activity": 0.0048, + "basaliob": 0.639, + "time": "2016-06-24T10:01:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.716 + }, { + "netbasalinsulin": 0.5, + "activity": 0.0052, + "basaliob": 0.628, + "time": "2016-06-24T10:06:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.691 + }, { + "netbasalinsulin": 0.5, + "activity": 0.0055, + "basaliob": 0.614, + "time": "2016-06-24T10:11:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.663 + }, { + "netbasalinsulin": 0.5, + "activity": 0.0059, + "basaliob": 0.596, + "time": "2016-06-24T10:16:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.633 + }, { + "netbasalinsulin": 0.55, + "activity": 0.0063, + "basaliob": 0.575, + "time": "2016-06-24T10:21:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.602 + }, { + "netbasalinsulin": 0.55, + "activity": 0.0067, + "basaliob": 0.549, + "time": "2016-06-24T10:26:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.568 + }, { + "netbasalinsulin": 0.55, + "activity": 0.0071, + "basaliob": 0.521, + "time": "2016-06-24T10:31:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.533 + }, { + "netbasalinsulin": 0.6, + "activity": 0.0074, + "basaliob": 0.489, + "time": "2016-06-24T10:36:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.496 + }, { + "netbasalinsulin": 0.6, + "activity": 0.0075, + "basaliob": 0.456, + "time": "2016-06-24T10:41:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.458 + }, { + "netbasalinsulin": 0.6, + "activity": 0.0075, + "basaliob": 0.42, + "time": "2016-06-24T10:46:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.421 + }, { + "netbasalinsulin": 0.6, + "activity": 0.0073, + "basaliob": 0.384, + "time": "2016-06-24T10:51:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.384 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0071, + "basaliob": 0.349, + "time": "2016-06-24T10:56:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.349 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0069, + "basaliob": 0.314, + "time": "2016-06-24T11:01:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.314 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0066, + "basaliob": 0.281, + "time": "2016-06-24T11:06:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.281 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0062, + "basaliob": 0.25, + "time": "2016-06-24T11:11:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.25 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0059, + "basaliob": 0.221, + "time": "2016-06-24T11:16:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.221 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0055, + "basaliob": 0.193, + "time": "2016-06-24T11:21:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.193 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0052, + "basaliob": 0.167, + "time": "2016-06-24T11:26:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.167 + }, { + "netbasalinsulin": 0.7, + "activity": 0.0049, + "basaliob": 0.143, + "time": "2016-06-24T11:31:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.143 + }, { + "netbasalinsulin": 0.7, + "activity": 0.0045, + "basaliob": 0.12, + "time": "2016-06-24T11:36:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.12 + }, { + "netbasalinsulin": 0.7, + "activity": 0.0041, + "basaliob": 0.1, + "time": "2016-06-24T11:41:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.1 + }, { + "netbasalinsulin": 0.7, + "activity": 0.0037, + "basaliob": 0.081, + "time": "2016-06-24T11:46:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.081 + }, { + "netbasalinsulin": 0.75, + "activity": 0.0034, + "basaliob": 0.064, + "time": "2016-06-24T11:51:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.064 + }, { + "netbasalinsulin": 0.75, + "activity": 0.003, + "basaliob": 0.049, + "time": "2016-06-24T11:56:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.049 + }, { + "netbasalinsulin": 0.8, + "activity": 0.0026, + "basaliob": 0.036, + "time": "2016-06-24T12:01:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.036 + }, { + "netbasalinsulin": 0.8, + "activity": 0.0021, + "basaliob": 0.026, + "time": "2016-06-24T12:06:16.000Z", + "hightempinsulin": 0.8, + "bolussnooze": 0, + "iob": 0.026 + }, { + "netbasalinsulin": 0.75, + "activity": 0.0017, + "basaliob": 0.017, + "time": "2016-06-24T12:11:16.000Z", + "hightempinsulin": 0.75, + "bolussnooze": 0, + "iob": 0.017 + }, { + "netbasalinsulin": 0.75, + "activity": 0.0013, + "basaliob": 0.011, + "time": "2016-06-24T12:16:16.000Z", + "hightempinsulin": 0.75, + "bolussnooze": 0, + "iob": 0.011 + }, { + "netbasalinsulin": 0.65, + "activity": 0.0009, + "basaliob": 0.006, + "time": "2016-06-24T12:21:16.000Z", + "hightempinsulin": 0.65, + "bolussnooze": 0, + "iob": 0.006 + }], + "enacted": { + "bg": 161, + "temp": "absolute", + "snoozeBG": 181, + "recieved": true, + "predBGs": { + "IOB": [161, 164, 166, 168, 170, 172, 174, 175, 176, 177, 177, 176, 175, 175, 174, 173, 173, 172, 172, 171, 171, 171, 171, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 168] + }, + "reason": "COB: undefined, Dev: 33, BGI: -2.56, ISF: 80, Target: 115; Eventual BG 181>=115, adj. req. rate:2.4 to maxSafeBasal:2.3, temp 1<2.3U/hr", + "rate": 2.25, + "eventualBG": 181, + "timestamp": "2016-06-24T09:19:06.000Z", + "duration": 30, + "tick": "+5", + "IOB": 0.166 + } + }, + "mmtune": { + "scanDetails": [ + ["916.564", 5, -78], + ["916.588", 3, -80], + ["916.612", 4, -68], + ["916.636", 5, -65], + ["916.660", 5, -60], + ["916.684", 5, -67], + ["916.708", 5, -71] + ], + "setFreq": 916.66, + "timestamp": "2016-06-24T09:26:22.000Z", + "usedDefault": false + }, + "created_at": "2016-06-24T09:27:49.230Z" + } +*/ diff --git a/plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/DeviceStatusExtensionKtTest.kt b/plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/DeviceStatusExtensionKtTest.kt new file mode 100644 index 0000000000..4e98d70982 --- /dev/null +++ b/plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/DeviceStatusExtensionKtTest.kt @@ -0,0 +1,86 @@ +package info.nightscout.plugins.sync.nsclientV3.extensions + +import dagger.android.AndroidInjector +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.TestBase +import info.nightscout.database.entities.DeviceStatus +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.configBuilder.RunningConfiguration +import info.nightscout.interfaces.constraints.Constraints +import info.nightscout.interfaces.iob.IobCobCalculator +import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData +import info.nightscout.interfaces.plugin.ActivePlugin +import info.nightscout.interfaces.profile.Instantiator +import info.nightscout.interfaces.profile.ProfileFunction +import info.nightscout.plugins.aps.APSResultObject +import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler +import info.nightscout.plugins.sync.nsclient.data.ProcessedDeviceStatusDataImpl +import info.nightscout.shared.interfaces.ResourceHelper +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.utils.DateUtil +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mock + +@Suppress("SpellCheckingInspection") +internal class DeviceStatusExtensionKtTest : TestBase() { + + @Mock lateinit var constraintChecker: Constraints + @Mock lateinit var sp: SP + @Mock lateinit var activePlugin: ActivePlugin + @Mock lateinit var iobCobCalculator: IobCobCalculator + @Mock lateinit var profileFunction: ProfileFunction + @Mock lateinit var rh: ResourceHelper + @Mock lateinit var dateUtil: DateUtil + @Mock lateinit var config: Config + @Mock lateinit var runningConfiguration: RunningConfiguration + @Mock lateinit var instantiator: Instantiator + + private lateinit var processedDeviceStatusData: ProcessedDeviceStatusData + private lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler + + val injector = HasAndroidInjector { + AndroidInjector { + if (it is APSResultObject) { + it.aapsLogger = aapsLogger + it.constraintChecker = constraintChecker + it.sp = sp + it.activePlugin = activePlugin + it.iobCobCalculator = iobCobCalculator + it.profileFunction = profileFunction + it.rh = rh + it.dateUtil = dateUtil + } + } + } + + @BeforeEach + fun setup() { + processedDeviceStatusData = ProcessedDeviceStatusDataImpl(rh, dateUtil, sp, instantiator) + nsDeviceStatusHandler = NSDeviceStatusHandler(sp, config, dateUtil, runningConfiguration, processedDeviceStatusData) + } + + @Test + fun dotest() { + + val deviceStatus = DeviceStatus( + timestamp = 10000, + suggested = "{\"temp\":\"absolute\",\"bg\":133,\"tick\":-6,\"eventualBG\":67,\"targetBG\":99,\"insulinReq\":0,\"deliverAt\":\"2023-01-02T15:29:33.374Z\",\"sensitivityRatio\":1,\"variable_sens\":97.5,\"predBGs\":{\"IOB\":[133,127,121,116,111,106,101,97,93,89,85,81,78,75,72,69,67,65,62,60,58,57,55,54,52,51,50,49,48,47,46,45,45,44,43,43,42,42,41,41,41,41,40,40,40,40,39],\"ZT\":[133,127,121,115,110,105,101,96,92,88,84,81,77,74,71,69,66,64,62,59,58,56,54,53,51,50,49,48,47,46,45,44,44,43,42,42,41,41,40,40,40,39,39,39,39,39,39,39],\"UAM\":[133,127,121,115,110,105,101,96,92,88,84,81,77,74,71,69,66,64,62,59,58,56,54,53,51,50,49,48,47,46,45,44,44,43,42,42,41,41,40,40,40,39]},\"reason\":\"COB: 0, Dev: 0.1, BGI: -0.3, ISF: 5.4, CR: 13, Target: 5.5, minPredBG 2.2, minGuardBG 2.1, IOBpredBG 2.2, UAMpredBG 2.2; minGuardBG 2.1<4.0\",\"COB\":0,\"IOB\":0.692,\"duration\":90,\"rate\":0,\"timestamp\":\"2023-01-02T15:29:39.460Z\"}", + iob = "{\"iob\":0.692,\"basaliob\":-0.411,\"activity\":0.0126,\"time\":\"2023-01-02T15:29:39.460Z\"}", + enacted = "{\"temp\":\"absolute\",\"bg\":133,\"tick\":-6,\"eventualBG\":67,\"targetBG\":99,\"insulinReq\":0,\"deliverAt\":\"2023-01-02T15:29:33.374Z\",\"sensitivityRatio\":1,\"variable_sens\":97.5,\"predBGs\":{\"IOB\":[133,127,121,116,111,106,101,97,93,89,85,81,78,75,72,69,67,65,62,60,58,57,55,54,52,51,50,49,48,47,46,45,45,44,43,43,42,42,41,41,41,41,40,40,40,40,39],\"ZT\":[133,127,121,115,110,105,101,96,92,88,84,81,77,74,71,69,66,64,62,59,58,56,54,53,51,50,49,48,47,46,45,44,44,43,42,42,41,41,40,40,40,39,39,39,39,39,39,39],\"UAM\":[133,127,121,115,110,105,101,96,92,88,84,81,77,74,71,69,66,64,62,59,58,56,54,53,51,50,49,48,47,46,45,44,44,43,42,42,41,41,40,40,40,39]},\"reason\":\"COB: 0, Dev: 0.1, BGI: -0.3, ISF: 5.4, CR: 13, Target: 5.5, minPredBG 2.2, minGuardBG 2.1, IOBpredBG 2.2, UAMpredBG 2.2; minGuardBG 2.1<4.0\",\"COB\":0,\"IOB\":0.692,\"duration\":90,\"rate\":0,\"timestamp\":\"2023-01-02T15:29:39.460Z\"}", + device = "openaps://samsung SM-G970F", + pump = "{\"battery\":{\"percent\":75},\"status\":{\"status\":\"normal\",\"timestamp\":\"2023-01-02T15:20:20.656Z\"},\"extended\":{\"Version\":\"3.1.0.3-dev-e-295e1ad18f-2022.12.24\"," + + "\"LastBolus\":\"02.01.23 15:24\",\"LastBolusAmount\":\"1\",\"TempBasalAbsoluteRate\":\"0\",\"TempBasalStart\":\"02.01.23 16:20\",\"TempBasalRemaining\":\"55\",\"BaseBasalRate\":\"0" + + ".41\"," + + "\"ActiveProfile\":\"L29_U200 IC\"},\"reservoir\":\"133\",\"clock\":\"2023-01-02T15:25:05.826Z\"}", + uploaderBattery = 60, + configuration = "{\"insulin\":5,\"insulinConfiguration\":{},\"sensitivity\":2,\"sensitivityConfiguration\":{\"openapsama_min_5m_carbimpact\":8,\"absorption_cutoff\":4,\"autosens_max\":1.2,\"autosens_min\":0.7},\"overviewConfiguration\":{\"units\":\"mmol\",\"QuickWizard\":\"[]\",\"eatingsoon_duration\":60,\"eatingsoon_target\":4,\"activity_duration\":180,\"activity_target\":7.5,\"hypo_duration\":90,\"hypo_target\":8,\"low_mark\":3.9,\"high_mark\":10,\"statuslights_cage_warning\":72,\"statuslights_cage_critical\":96,\"statuslights_iage_warning\":120,\"statuslights_iage_critical\":150,\"statuslights_sage_warning\":168,\"statuslights_sage_critical\":336,\"statuslights_sbat_warning\":25,\"statuslights_sbat_critical\":5,\"statuslights_bage_warning\":720,\"statuslights_bage_critical\":800,\"statuslights_res_warning\":30,\"statuslights_res_critical\":10,\"statuslights_bat_warning\":50,\"statuslights_bat_critical\":25,\"boluswizard_percentage\":70},\"safetyConfiguration\":{\"age\":\"resistantadult\",\"treatmentssafety_maxbolus\":10,\"treatmentssafety_maxcarbs\":70}}" + ) + + val remoteDeviceStatus = deviceStatus.toRemoteDeviceStatus() + + nsDeviceStatusHandler.handleNewData(arrayOf(remoteDeviceStatus)) + Assertions.assertEquals(75, processedDeviceStatusData.pumpData?.percent) + } +} \ No newline at end of file diff --git a/plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/OfflineEventKtTest.kt b/plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/OfflineEventExtensionKtTest.kt similarity index 98% rename from plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/OfflineEventKtTest.kt rename to plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/OfflineEventExtensionKtTest.kt index e4883fbb6d..34137f047c 100644 --- a/plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/OfflineEventKtTest.kt +++ b/plugins/sync/src/test/java/info/nightscout/plugins/sync/nsclientV3/extensions/OfflineEventExtensionKtTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @Suppress("SpellCheckingInspection") -internal class OfflineEventKtTest { +internal class OfflineEventExtensionKtTest { @Test fun toOfflineEvent() {