Merge branch 'dev' into smoothing

This commit is contained in:
Milos Kozak 2022-12-22 16:04:12 +01:00
commit 37510088c5
101 changed files with 1465 additions and 730 deletions

View file

@ -349,6 +349,7 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
message += "${rh.gs(info.nightscout.configuration.R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.getVersion()}"
if (config.isEngineeringMode()) message += "\n${rh.gs(info.nightscout.configuration.R.string.engineering_mode_enabled)}"
if (config.isUnfinishedMode()) message += "\nUnfinished mode enabled"
if (!fabricPrivacy.fabricEnabled()) message += "\n${rh.gs(R.string.fabric_upload_disabled)}"
message += rh.gs(info.nightscout.pump.combo.R.string.about_link_urls)
val messageSpanned = SpannableString(message)

View file

@ -11,6 +11,7 @@ import info.nightscout.androidaps.insight.database.InsightDatabaseDao
import info.nightscout.androidaps.insight.database.InsightDbHelper
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.ApsMode
import info.nightscout.implementation.iob.GlucoseStatusProviderImpl
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
import info.nightscout.interfaces.constraints.Constraint
@ -274,13 +275,13 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
// 2x Safety & Objectives
@Test
fun isClosedLoopAllowedTest() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("closed")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.CLOSED.name)
objectivesPlugin.objectives[Objectives.MAXIOB_ZERO_CL_OBJECTIVE].startedOn = 0
var c: Constraint<Boolean> = constraintChecker.isClosedLoopAllowed()
aapsLogger.debug("Reason list: " + c.reasonList.toString())
// Assertions.assertTrue(c.reasonList[0].toString().contains("Closed loop is disabled")) // Safety & Objectives
Assertions.assertEquals(false, c.value())
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("open")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.OPEN.name)
c = constraintChecker.isClosedLoopAllowed()
Assertions.assertTrue(c.reasonList[0].contains("Closed loop mode disabled in preferences")) // Safety & Objectives
// Assertions.assertEquals(3, c.reasonList.size) // 2x Safety & Objectives
@ -323,7 +324,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
openAPSSMBPlugin.setPluginEnabled(PluginType.APS, true)
objectivesPlugin.objectives[Objectives.SMB_OBJECTIVE].startedOn = 0
`when`(sp.getBoolean(info.nightscout.plugins.aps.R.string.key_use_smb, false)).thenReturn(false)
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("open")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.OPEN.name)
// `when`(constraintChecker.isClosedLoopAllowed()).thenReturn(Constraint(true))
val c = constraintChecker.isSMBModeEnabled()
Assertions.assertEquals(true, c.reasonList.size == 3) // 2x Safety & Objectives
@ -430,7 +431,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
@Test
fun iobAMAShouldBeLimited() {
// No limit by default
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("closed")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.CLOSED.name)
`when`(sp.getDouble(info.nightscout.plugins.aps.R.string.key_openapsma_max_iob, 1.5)).thenReturn(1.5)
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("teenage")
openAPSAMAPlugin.setPluginEnabled(PluginType.APS, true)
@ -446,7 +447,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
@Test
fun iobSMBShouldBeLimited() {
// No limit by default
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("closed")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.CLOSED.name)
`when`(sp.getDouble(info.nightscout.plugins.aps.R.string.key_openapssmb_max_iob, 3.0)).thenReturn(3.0)
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("teenage")
openAPSSMBPlugin.setPluginEnabled(PluginType.APS, true)

View file

@ -7,6 +7,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.ApsMode
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.configBuilder.RunningConfiguration
import info.nightscout.interfaces.constraints.Constraints
@ -70,7 +71,7 @@ class LoopPluginTest : TestBase() {
fun testPluginInterface() {
`when`(rh.gs(info.nightscout.core.ui.R.string.loop)).thenReturn("Loop")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.loop_shortname)).thenReturn("LOOP")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("closed")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.CLOSED.name)
val pumpDescription = PumpDescription()
`when`(virtualPumpPlugin.pumpDescription).thenReturn(pumpDescription)
Assert.assertEquals(LoopFragment::class.java.name, loopPlugin.pluginDescription.fragmentClass)

View file

@ -5,6 +5,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.HardLimitsMock
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.ApsMode
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
import info.nightscout.interfaces.constraints.Constraint
@ -98,7 +99,7 @@ class SafetyPluginTest : TestBaseWithProfile() {
@Test
fun disabledEngineeringModeShouldLimitClosedLoop() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("closed")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.CLOSED.name)
`when`(config.isEngineeringModeOrRelease()).thenReturn(false)
var c = Constraint(true)
c = safetyPlugin.isClosedLoopAllowed(c)
@ -108,7 +109,7 @@ class SafetyPluginTest : TestBaseWithProfile() {
@Test
fun setOpenLoopInPreferencesShouldLimitClosedLoop() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("open")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.OPEN.name)
var c = Constraint(true)
c = safetyPlugin.isClosedLoopAllowed(c)
Assertions.assertTrue(c.getReasons(aapsLogger).contains("Closed loop mode disabled in preferences"))
@ -278,7 +279,7 @@ Safety: Limiting max basal rate to 500.00 U/h because of pump limit
openAPSSMBPlugin.setPluginEnabled(PluginType.APS, true)
//`when`(openAPSSMBPlugin.isEnabled()).thenReturn(true)
//`when`(openAPSAMAPlugin.isEnabled()).thenReturn(false)
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("lgs")
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.LGS.name)
`when`(sp.getDouble(info.nightscout.plugins.aps.R.string.key_openapsma_max_iob, 1.5)).thenReturn(1.5)
`when`(sp.getDouble(info.nightscout.plugins.aps.R.string.key_openapssmb_max_iob, 3.0)).thenReturn(3.0)
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("teenage")

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
</manifest>

View file

@ -0,0 +1,13 @@
package info.nightscout.interfaces
enum class ApsMode() {
OPEN,
CLOSED,
LGS,
UNDEFINED;
companion object {
fun fromString(stringValue: String?) = values().firstOrNull { it.name == stringValue?.uppercase() } ?: UNDEFINED
}
}

View file

@ -17,21 +17,24 @@ import org.json.JSONObject
interface DataSyncSelector {
interface DataPair
data class PairTemporaryTarget(val value: TemporaryTarget, val updateRecordId: Long): DataPair
data class PairGlucoseValue(val value: GlucoseValue, val updateRecordId: Long): DataPair
data class PairTherapyEvent(val value: TherapyEvent, val updateRecordId: Long): DataPair
data class PairFood(val value: Food, val updateRecordId: Long): DataPair
data class PairBolus(val value: Bolus, val updateRecordId: Long): DataPair
data class PairCarbs(val value: Carbs, val updateRecordId: Long): DataPair
data class PairBolusCalculatorResult(val value: BolusCalculatorResult, val updateRecordId: Long): DataPair
data class PairTemporaryBasal(val value: TemporaryBasal, val updateRecordId: Long): DataPair
data class PairExtendedBolus(val value: ExtendedBolus, val updateRecordId: Long): DataPair
data class PairProfileSwitch(val value: ProfileSwitch, val updateRecordId: Long): DataPair
data class PairEffectiveProfileSwitch(val value: EffectiveProfileSwitch, val updateRecordId: Long): DataPair
data class PairOfflineEvent(val value: OfflineEvent, val updateRecordId: Long): DataPair
data class PairProfileStore(val value: JSONObject, val timestampSync: Long): DataPair
data class PairDeviceStatus(val value: DeviceStatus, val unused: Long?): DataPair
interface DataPair {
val value: Any
val id: Long
}
data class PairTemporaryTarget(override val value: TemporaryTarget, override val id: Long): DataPair
data class PairGlucoseValue(override val value: GlucoseValue, override val id: Long): DataPair
data class PairTherapyEvent(override val value: TherapyEvent, override val id: Long): DataPair
data class PairFood(override val value: Food, override val id: Long): DataPair
data class PairBolus(override val value: Bolus, override val id: Long): DataPair
data class PairCarbs(override val value: Carbs, override val id: Long): DataPair
data class PairBolusCalculatorResult(override val value: BolusCalculatorResult, override val id: Long): DataPair
data class PairTemporaryBasal(override val value: TemporaryBasal, override val id: Long): DataPair
data class PairExtendedBolus(override val value: ExtendedBolus, override val id: Long): DataPair
data class PairProfileSwitch(override val value: ProfileSwitch, override val id: Long): DataPair
data class PairEffectiveProfileSwitch(override val value: EffectiveProfileSwitch, override val id: Long): DataPair
data class PairOfflineEvent(override val value: OfflineEvent, override val id: Long): DataPair
data class PairProfileStore(override val value: JSONObject, override val id: Long): DataPair
data class PairDeviceStatus(override val value: DeviceStatus, override val id: Long): DataPair
fun queueSize(): Long

View file

@ -16,7 +16,31 @@ interface NsClient : Sync {
fun textLog(): Spanned
fun clearLog()
enum class Collection { ENTRIES, TREATMENTS}
/**
* NSC v3 does first load of all data
* next loads are using srvModified property for sync
* not used for NSCv1
*
* @return true if inside first load of NSCv3, true for NSCv1
*/
fun isFirstLoad(collection: Collection): Boolean = true
/**
* Update newest loaded timestamp for entries collection (first load or NSCv1)
* Update newest srvModified (sync loads)
*
* @param latestReceived timestamp
*
*/
fun updateLatestBgReceivedIfNewer(latestReceived: Long)
/**
* Update newest loaded timestamp for treatments collection (first load or NSCv1)
* Update newest srvModified (sync loads)
*
* @param latestReceived timestamp
*
*/
fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long)
fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long)

View file

@ -1,28 +0,0 @@
package info.nightscout.interfaces.utils
import android.content.Context
import android.content.Intent
import android.provider.AlarmClock
import info.nightscout.androidaps.annotations.OpenForTesting
import javax.inject.Inject
import javax.inject.Singleton
@OpenForTesting
@Singleton
open class TimerUtil @Inject constructor(
private val context: Context
) {
/**
* Schedule alarm in @seconds
*/
fun scheduleReminder(seconds: Int, text: String) {
Intent(AlarmClock.ACTION_SET_TIMER).apply {
flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK
putExtra(AlarmClock.EXTRA_LENGTH, seconds)
putExtra(AlarmClock.EXTRA_SKIP_UI, true)
putExtra(AlarmClock.EXTRA_MESSAGE, text)
context.startActivity(this)
}
}
}

View file

@ -4,7 +4,7 @@ import android.content.Context
import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException
import info.nightscout.sdk.exceptions.InvalidAccessTokenException
import info.nightscout.sdk.exceptions.InvalidFormatNightscoutException
import info.nightscout.sdk.exceptions.TodoNightscoutException
import info.nightscout.sdk.exceptions.UnsuccessfullNightscoutException
import info.nightscout.sdk.exceptions.UnknownResponseNightscoutException
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.Status
@ -101,9 +101,9 @@ class NSAndroidClientImpl(
val response = api.lastModified()
if (response.isSuccessful) {
return@callWrapper response.body()?.result ?: throw TodoNightscoutException()
return@callWrapper response.body()?.result ?: throw UnsuccessfullNightscoutException()
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}
@ -114,17 +114,19 @@ class NSAndroidClientImpl(
if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}
override suspend fun getSgvsModifiedSince(from: Long): List<NSSgvV3> = callWrapper(dispatcher) {
override suspend fun getSgvsModifiedSince(from: Long, limit: Long): NSAndroidClient.ReadResponse<List<NSSgvV3>> = callWrapper(dispatcher) {
val response = api.getSgvsModifiedSince(from)
val response = api.getSgvsModifiedSince(from, limit)
val eTagString = response.headers()["ETag"]
val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong() ?: throw UnsuccessfullNightscoutException()
if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
return@callWrapper NSAndroidClient.ReadResponse(eTag, response.body()?.result?.map(RemoteEntry::toSgv).toNotNull())
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}
@ -134,17 +136,29 @@ class NSAndroidClientImpl(
if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}
override suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): List<NSTreatment> = callWrapper(dispatcher) {
override suspend fun getTreatmentsNewerThan(from: Long, limit: Long): List<NSTreatment> = callWrapper(dispatcher) {
val response = api.getTreatmentsModifiedSince(from, limit)
val response = api.getTreatmentsNewerThan(from, limit)
if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull()
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}
override suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): NSAndroidClient.ReadResponse<List<NSTreatment>> = callWrapper(dispatcher) {
val response = api.getTreatmentsModifiedSince(from, limit)
val eTagString = response.headers()["ETag"]
val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong() ?: throw UnsuccessfullNightscoutException()
if (response.isSuccessful) {
return@callWrapper NSAndroidClient.ReadResponse(eTag, response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull())
} else {
throw UnsuccessfullNightscoutException()
}
}
@ -154,23 +168,25 @@ class NSAndroidClientImpl(
if (response.isSuccessful) {
return@callWrapper response.body()?.result.toNotNull()
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}
override suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse = callWrapper(dispatcher) {
val remoteTreatment = nsTreatment.toRemoteTreatment() ?: throw InvalidFormatNightscoutException()
remoteTreatment.app = "AAPS"
val response = api.createTreatment(remoteTreatment)
if (response.isSuccessful) {
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 TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}
@ -180,13 +196,14 @@ class NSAndroidClientImpl(
val response = api.updateTreatment(remoteTreatment)
if (response.isSuccessful) {
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 TodoNightscoutException() // TODO: react to response errors (offline, ...)
throw UnsuccessfullNightscoutException()
}
}

View file

@ -9,14 +9,15 @@ 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<String> = rxSingle { client.getVersion() }
override fun getStatus(): Single<Status> = rxSingle { client.getStatus() }
override fun getLastModified(): Single<LastModified> = rxSingle { client.getLastModified() }
override fun getSgvsModifiedSince(from: Long): Single<List<NSSgvV3>> = rxSingle { client.getSgvsModifiedSince(from) }
override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<List<NSTreatment>> =
override fun getSgvsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSSgvV3>>> = rxSingle { client.getSgvsModifiedSince(from, limit) }
override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>> =
rxSingle { client.getTreatmentsModifiedSince(from, limit) }
override fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>> =
rxSingle { client.getDeviceStatusModifiedSince(from) }

View file

@ -1,3 +0,0 @@
package info.nightscout.sdk.exceptions
class TodoNightscoutException : NightscoutException()

View file

@ -0,0 +1,3 @@
package info.nightscout.sdk.exceptions
class UnsuccessfullNightscoutException : NightscoutException()

View file

@ -9,6 +9,11 @@ import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
interface NSAndroidClient {
class ReadResponse<T>(
val lastServerModified: Long,
val values: T
)
val lastStatus: Status?
suspend fun getVersion(): String
suspend fun getStatus(): Status
@ -16,9 +21,10 @@ interface NSAndroidClient {
suspend fun getLastModified(): LastModified
suspend fun getSgvs(): List<NSSgvV3>
suspend fun getSgvsModifiedSince(from: Long): List<NSSgvV3>
suspend fun getSgvsModifiedSince(from: Long, limit: Long): ReadResponse<List<NSSgvV3>>
suspend fun getSgvsNewerThan(from: Long, limit: Long): List<NSSgvV3>
suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): List<NSTreatment>
suspend fun getTreatmentsNewerThan(from: Long, limit: Long): List<NSTreatment>
suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): ReadResponse<List<NSTreatment>>
suspend fun getDeviceStatusModifiedSince(from: Long): List<RemoteDeviceStatus>
suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse
suspend fun updateTreatment(nsTreatment: NSTreatment): CreateUpdateResponse

View file

@ -12,8 +12,8 @@ interface NSAndroidRxClient {
fun getVersion(): Single<String>
fun getStatus(): Single<Status>
fun getLastModified(): Single<LastModified>
fun getSgvsModifiedSince(from: Long): Single<List<NSSgvV3>>
fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<List<NSTreatment>>
fun getSgvsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSSgvV3>>>
fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>>
fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>>
}

View file

@ -1,6 +1,7 @@
package info.nightscout.sdk.localmodel.treatment
class CreateUpdateResponse(
val response: Int,
val identifier: String?,
val isDeduplication: Boolean? = false,
val deduplicatedIdentifier: String? = null,

View file

@ -19,6 +19,7 @@ data class NSBolus(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val insulin: Double,
val type: BolusType

View file

@ -1,7 +1,6 @@
package info.nightscout.sdk.localmodel.treatment
import info.nightscout.sdk.localmodel.entry.NsUnits
import org.json.JSONObject
data class NSBolusWizard(
override val date: Long,
@ -20,6 +19,7 @@ data class NSBolusWizard(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val bolusCalculatorResult: String?,
val glucose: Double?,
) : NSTreatment

View file

@ -4,14 +4,14 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
data class NSCarbs(
override val date: Long,
override val device: String?,
override val device: String? = null,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long?,
override val srvCreated: Long?,
override val units: NsUnits? = null,
override val srvModified: Long? = null,
override val srvCreated: Long? = null,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
override val subject: String? = null,
override var isReadOnly: Boolean = false,
override val isValid: Boolean,
override val eventType: EventType,
override val notes: String?,
@ -19,6 +19,7 @@ data class NSCarbs(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val carbs: Double,
val duration: Long
val duration: Long?
) : NSTreatment

View file

@ -5,14 +5,14 @@ import org.json.JSONObject
data class NSEffectiveProfileSwitch(
override val date: Long,
override val device: String?,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long?,
override val srvCreated: Long?,
override val device: String? = null,
override val identifier: String? = null,
override val units: NsUnits? = null,
override val srvModified: Long? = null,
override val srvCreated: Long? = null,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
override val subject: String? = null ,
override var isReadOnly: Boolean = false,
override val isValid: Boolean,
override val eventType: EventType,
override val notes: String?,
@ -20,6 +20,7 @@ data class NSEffectiveProfileSwitch(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val profileJson: JSONObject,
val originalProfileName: String,
val originalCustomizedName: String,

View file

@ -19,6 +19,7 @@ data class NSExtendedBolus(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val enteredinsulin: Double,
val isEmulatingTempBasal: Boolean?

View file

@ -19,6 +19,7 @@ data class NSOfflineEvent(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val reason: Reason
) : NSTreatment {

View file

@ -5,23 +5,24 @@ import org.json.JSONObject
data class NSProfileSwitch(
override val date: Long,
override val device: String?,
override val device: String? = null,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long?,
override val srvCreated: Long?,
override val units: NsUnits? = null,
override val srvModified: Long? = null,
override val srvCreated: Long? = null,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
override val subject: String? = null,
override var isReadOnly: Boolean = false,
override val isValid: Boolean,
override val eventType: EventType,
override val notes: String?,
override val notes: String? = null,
override val pumpId: Long?,
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val profileJson: JSONObject?,
val profileName: String,
val profile: String,
val originalProfileName: String?,
val timeShift: Long?,
val percentage: Int?,

View file

@ -1,7 +1,6 @@
package info.nightscout.sdk.localmodel.treatment
import info.nightscout.sdk.localmodel.entry.NsUnits
import org.json.JSONObject
data class NSTemporaryBasal(
override val date: Long,
@ -20,6 +19,7 @@ data class NSTemporaryBasal(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val rate: Double, // when sending to NS always convertedToAbsolute(timestamp, profile)
val isAbsolute: Boolean,

View file

@ -19,6 +19,7 @@ data class NSTemporaryTarget(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val targetBottom: Double,
val targetTop: Double,

View file

@ -20,6 +20,7 @@ data class NSTherapyEvent(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
var enteredBy: String? = null,
var glucose: Double? = null,

View file

@ -19,6 +19,7 @@ interface NSTreatment {
val endId: Long?
val pumpType: String?
val pumpSerial: String?
var app: String?
fun Double.asMgdl() =
when (units) {

View file

@ -210,7 +210,7 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
pumpType = this.pumpType,
pumpSerial = this.pumpSerial,
profileJson = this.profileJson?.let { JSONObject(this.profileJson) },
profileName = this.profile,
profile = this.profile,
originalProfileName = this.originalProfileName,
originalDuration = this.originalDuration,
duration = this.duration,
@ -496,7 +496,7 @@ internal fun NSTreatment.toRemoteTreatment(): RemoteTreatment? =
pumpType = pumpType,
pumpSerial = pumpSerial,
profileJson = profileJson.toString(), // must be de-customized
profile = profileName,
profile = profile,
originalProfileName = originalProfileName,
originalDuration = originalDuration,
duration = duration,

View file

@ -2,18 +2,15 @@ package info.nightscout.sdk.networking
import com.google.gson.JsonElement
import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
import info.nightscout.sdk.remotemodel.NSResponse
import info.nightscout.sdk.remotemodel.RemoteCreateUpdateResponse
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
import info.nightscout.sdk.remotemodel.RemoteEntry
import info.nightscout.sdk.remotemodel.RemoteStatusResponse
import info.nightscout.sdk.remotemodel.RemoteTreatment
import okhttp3.RequestBody
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
@ -48,7 +45,10 @@ internal interface NightscoutRemoteService {
suspend fun getSgvsNewerThan(@Query(value = "date\$gt", encoded = true) date: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteEntry>>>
@GET("v3/entries/history/{from}")
suspend fun getSgvsModifiedSince(@Path("from") from: Long): Response<NSResponse<List<RemoteEntry>>>
suspend fun getSgvsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteEntry>>>
@GET("v3/treatments")
suspend fun getTreatmentsNewerThan(@Query(value = "date\$gt", encoded = true) date: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteTreatment>>>
@GET("v3/treatments/history/{from}")
suspend fun getTreatmentsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteTreatment>>>
@ -57,9 +57,9 @@ internal interface NightscoutRemoteService {
suspend fun getDeviceStatusModifiedSince(@Path("from") from: Long): Response<NSResponse<List<RemoteDeviceStatus>>>
@POST("v3/treatments")
fun createTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun createTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
@PUT("v3/treatments")
fun updateTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun updateTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
}

View file

@ -15,9 +15,9 @@ data class LastModified(
@Serializable
data class Collections(
@SerializedName("devicestatus") var devicestatus: Long, // devicestatus collection
@SerializedName("entries") var entries: Long, // entries collection
@SerializedName("profile") var profile: Long, // profile collection
@SerializedName("treatments") var treatments: Long // treatments collection
@SerializedName("devicestatus") var devicestatus: Long = 0, // devicestatus collection
@SerializedName("entries") var entries: Long = 0, // entries collection
@SerializedName("profile") var profile: Long = 0, // profile collection
@SerializedName("treatments") var treatments: Long = 0 // treatments collection
)
}

View file

@ -1,11 +1,9 @@
package info.nightscout.sdk.remotemodel
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import info.nightscout.sdk.localmodel.treatment.EventType
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
import org.json.JSONObject
/*
* Depending on the type, different other fields are present.
@ -23,7 +21,7 @@ internal data class RemoteTreatment(
@SerializedName("timestamp") val timestamp: Long? = null, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("created_at") val created_at: String? = null, // integer($int64) 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("utcOffset") val utcOffset: Long? = null, // integer Local UTC offset (timezone) of the event in minutes. This field can be set either directly by the client (in the incoming document) or it is automatically parsed from the date field.
// @SerializedName("app") val app : String, // TODO required ? Application or system in which the record was entered by human or device for the first time.
@SerializedName("app") var app : String? = null, // Application or system in which the record was entered by human or device for the first time.
@SerializedName("device") val device: String? = null, // string The device from which the data originated (including serial number of the device, if it is relevant and safe).
@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("subject") val subject: String? = null, // string Name of the security subject (within Nightscout scope) which has created the document. This field is automatically set by the server from the passed token or JWT.

View file

@ -31,6 +31,7 @@ import info.nightscout.database.impl.transactions.InsertAndCancelCurrentOfflineE
import info.nightscout.database.impl.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.ApsMode
import info.nightscout.interfaces.aps.APSResult
import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.aps.Loop.LastRun
@ -182,10 +183,10 @@ class LoopPlugin @Inject constructor(
get() {
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
val maxIobAllowed = constraintChecker.getMaxIOBAllowed().value()
val apsMode = sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")
val apsMode = ApsMode.fromString(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name))
val pump = activePlugin.activePump
var isLGS = false
if (!isSuspended && !pump.isSuspended()) if (closedLoopEnabled.value()) if (maxIobAllowed == HardLimits.MAX_IOB_LGS || apsMode == "lgs") isLGS = true
if (!isSuspended && !pump.isSuspended()) if (closedLoopEnabled.value()) if (maxIobAllowed == HardLimits.MAX_IOB_LGS || apsMode == ApsMode.LGS) isLGS = true
return isLGS
}

View file

@ -385,7 +385,7 @@ class AutotuneFragment : DaggerFragment() {
private val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
updateGui()
_binding?.let { updateGui() }
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

View file

@ -53,7 +53,7 @@ import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.utils.TimerUtil
import info.nightscout.automation.ui.TimerUtil
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventBTChange

View file

@ -12,7 +12,7 @@ import info.nightscout.interfaces.Config
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.interfaces.utils.TimerUtil
import info.nightscout.automation.ui.TimerUtil
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.utils.DateUtil
import org.json.JSONObject

View file

@ -0,0 +1,36 @@
package info.nightscout.automation.ui
import android.content.Context
import android.content.Intent
import android.provider.AlarmClock
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.automation.R
import info.nightscout.core.ui.toast.ToastUtils
import javax.inject.Inject
import javax.inject.Singleton
@OpenForTesting
@Singleton
open class TimerUtil @Inject constructor(
private val context: Context
) {
/**
* Schedule alarm in android
*
* @param seconds
*/
fun scheduleReminder(seconds: Int, text: String) {
try {
Intent(AlarmClock.ACTION_SET_TIMER).apply {
flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK
putExtra(AlarmClock.EXTRA_LENGTH, seconds)
putExtra(AlarmClock.EXTRA_SKIP_UI, true)
putExtra(AlarmClock.EXTRA_MESSAGE, text)
context.startActivity(this)
}
} catch (e: Exception) {
ToastUtils.errorToast(context, R.string.error_setting_reminder)
}
}
}

View file

@ -134,5 +134,6 @@
<!-- Reminders-->
<string name="time_to_eat">Time to eat!\nRun Bolus wizard and do calculation again.</string>
<string name="time_to_bolus">Time to bolus!\nRun Bolus wizard and do calculation again.</string>
<string name="error_setting_reminder">Error while setting future alarm</string>
</resources>

View file

@ -13,7 +13,7 @@ import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.utils.TimerUtil
import info.nightscout.automation.ui.TimerUtil
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP

View file

@ -13,7 +13,7 @@ import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.utils.TimerUtil
import info.nightscout.automation.ui.TimerUtil
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP

View file

@ -9,7 +9,7 @@ import info.nightscout.automation.elements.InputString
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.utils.TimerUtil
import info.nightscout.automation.ui.TimerUtil
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil

View file

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

View file

@ -2,24 +2,38 @@ package info.nightscout.plugins.constraints.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.plugins.constraints.objectives.ObjectivesFragment
import info.nightscout.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.plugins.constraints.objectives.dialogs.NtpProgressDialog
import info.nightscout.plugins.constraints.objectives.objectives.Objective
import info.nightscout.plugins.constraints.objectives.objectives.Objective0
import info.nightscout.plugins.constraints.objectives.objectives.Objective1
import info.nightscout.plugins.constraints.objectives.objectives.Objective10
import info.nightscout.plugins.constraints.objectives.objectives.Objective2
import info.nightscout.plugins.constraints.objectives.objectives.Objective3
import info.nightscout.plugins.constraints.objectives.objectives.Objective4
import info.nightscout.plugins.constraints.objectives.objectives.Objective5
import info.nightscout.plugins.constraints.objectives.objectives.Objective6
import info.nightscout.plugins.constraints.objectives.objectives.Objective7
import info.nightscout.plugins.constraints.objectives.objectives.Objective9
@Module
@Suppress("unused")
abstract class ObjectivesModule {
@ContributesAndroidInjector abstract fun contributesObjectivesFragment(): info.nightscout.plugins.constraints.objectives.ObjectivesFragment
@ContributesAndroidInjector abstract fun contributesObjectivesExamDialog(): info.nightscout.plugins.constraints.objectives.activities.ObjectivesExamDialog
@ContributesAndroidInjector abstract fun contributesNtpProgressDialog(): info.nightscout.plugins.constraints.objectives.dialogs.NtpProgressDialog
@ContributesAndroidInjector abstract fun contributesObjectivesFragment(): ObjectivesFragment
@ContributesAndroidInjector abstract fun contributesObjectivesExamDialog(): ObjectivesExamDialog
@ContributesAndroidInjector abstract fun contributesNtpProgressDialog(): NtpProgressDialog
@ContributesAndroidInjector abstract fun objectiveInjector(): info.nightscout.plugins.constraints.objectives.objectives.Objective
@ContributesAndroidInjector abstract fun objective0Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective0
@ContributesAndroidInjector abstract fun objective1Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective1
@ContributesAndroidInjector abstract fun objective2Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective2
@ContributesAndroidInjector abstract fun objective3Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective3
@ContributesAndroidInjector abstract fun objective4Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective4
@ContributesAndroidInjector abstract fun objective5Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective5
@ContributesAndroidInjector abstract fun objective6Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective6
@ContributesAndroidInjector abstract fun objective7Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective7
@ContributesAndroidInjector abstract fun objective9Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective9
@ContributesAndroidInjector abstract fun objective10Injector(): info.nightscout.plugins.constraints.objectives.objectives.Objective10
@ContributesAndroidInjector abstract fun objectiveInjector(): Objective
@ContributesAndroidInjector abstract fun objective0Injector(): Objective0
@ContributesAndroidInjector abstract fun objective1Injector(): Objective1
@ContributesAndroidInjector abstract fun objective2Injector(): Objective2
@ContributesAndroidInjector abstract fun objective3Injector(): Objective3
@ContributesAndroidInjector abstract fun objective4Injector(): Objective4
@ContributesAndroidInjector abstract fun objective5Injector(): Objective5
@ContributesAndroidInjector abstract fun objective6Injector(): Objective6
@ContributesAndroidInjector abstract fun objective7Injector(): Objective7
@ContributesAndroidInjector abstract fun objective9Injector(): Objective9
@ContributesAndroidInjector abstract fun objective10Injector(): Objective10
}

View file

@ -1,6 +1,7 @@
package info.nightscout.plugins.constraints.objectives
import android.annotation.SuppressLint
import android.graphics.Typeface
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
@ -159,13 +160,13 @@ class ObjectivesFragment : DaggerFragment() {
if (objective.objective != 0) {
holder.binding.objective.visibility = View.VISIBLE
holder.binding.objective.text = rh.gs(objective.objective)
} else
holder.binding.objective.visibility = View.GONE
} else holder.binding.objective.visibility = View.GONE
if (objective.gate != 0) {
holder.binding.gate.visibility = View.VISIBLE
holder.binding.gate.text = rh.gs(objective.gate)
} else
holder.binding.gate.visibility = View.GONE
} else holder.binding.gate.visibility = View.GONE
if (!objective.isStarted) {
holder.binding.gate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
holder.binding.verify.visibility = View.GONE
@ -173,6 +174,8 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.accomplished.visibility = View.GONE
holder.binding.unfinish.visibility = View.GONE
holder.binding.unstart.visibility = View.GONE
holder.binding.learnedLabel.visibility = View.GONE
holder.binding.learned.removeAllViews()
if (position == 0 || objectivesPlugin.allPriorAccomplished(position))
holder.binding.start.visibility = View.VISIBLE
else
@ -185,6 +188,14 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.accomplished.visibility = View.VISIBLE
holder.binding.unfinish.visibility = View.VISIBLE
holder.binding.unstart.visibility = View.GONE
holder.binding.learnedLabel.visibility = View.VISIBLE
holder.binding.learned.removeAllViews()
for (task in objective.tasks) {
if (task.shouldBeIgnored()) continue
for (learned in task.learned) {
holder.binding.learned.addView(TextView(context).also { it.text = rh.gs(learned.learned) })
}
}
} else if (objective.isStarted) {
holder.binding.gate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
holder.binding.verify.visibility = View.VISIBLE
@ -195,12 +206,14 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.unstart.visibility = View.VISIBLE
holder.binding.progress.visibility = View.VISIBLE
holder.binding.progress.removeAllViews()
holder.binding.learnedLabel.visibility = View.GONE
holder.binding.learned.removeAllViews()
for (task in objective.tasks) {
if (task.shouldBeIgnored()) continue
// name
val name = TextView(holder.binding.progress.context)
name.text = "${rh.gs(task.task)}:"
name.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor) )
name.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
holder.binding.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
// hint
task.hints.forEach { h ->
@ -226,6 +239,16 @@ class ObjectivesFragment : DaggerFragment() {
dialog.show(childFragmentManager, "ObjectivesFragment")
}
}
if (task.isCompleted()) {
if (task.learned.isNotEmpty())
holder.binding.progress.addView(
TextView(context).also {
it.text = rh.gs(R.string.what_i_ve_learned)
it.setTypeface(it.typeface, Typeface.BOLD)
})
for (learned in task.learned)
holder.binding.progress.addView(TextView(context).also { it.text = rh.gs(learned.learned) })
}
// horizontal line
val separator = View(holder.binding.progress.context)
separator.setBackgroundColor(rh.gac(context, info.nightscout.core.ui.R.attr.separatorColor))
@ -325,26 +348,6 @@ class ObjectivesFragment : DaggerFragment() {
rxBus.send(EventObjectivesUpdateGui())
rxBus.send(EventSWUpdate(false))
}
if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted && objective.specialActionEnabled()) {
// generate random request code if none exists
val request = sp.getString(R.string.key_objectives_request_code, String.format("%1$05d", (Math.random() * 99999).toInt()))
sp.putString(R.string.key_objectives_request_code, request)
holder.binding.requestcode.text = rh.gs(R.string.requestcode, request)
holder.binding.requestcode.visibility = View.VISIBLE
holder.binding.enterbutton.visibility = View.VISIBLE
holder.binding.input.visibility = View.VISIBLE
holder.binding.inputhint.visibility = View.VISIBLE
holder.binding.enterbutton.setOnClickListener {
val input = holder.binding.input.text.toString()
activity?.let { activity -> objective.specialAction(activity, input) }
rxBus.send(EventObjectivesUpdateGui())
}
} else {
holder.binding.enterbutton.visibility = View.GONE
holder.binding.input.visibility = View.GONE
holder.binding.inputhint.visibility = View.GONE
holder.binding.requestcode.visibility = View.GONE
}
}
override fun getItemCount(): Int {

View file

@ -37,8 +37,6 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
var tasks: MutableList<Task> = ArrayList()
var hasSpecialInput = false
val isCompleted: Boolean
get() {
for (task in tasks) {
@ -81,6 +79,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
abstract inner class Task(var objective: Objective, @StringRes val task: Int) {
var hints = ArrayList<Hint>()
var learned = ArrayList<Learned>()
abstract fun isCompleted(): Boolean
@ -94,6 +93,11 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
return this
}
fun learned(learned: Learned): Task {
this.learned.add(learned)
return this
}
open fun shouldBeIgnored(): Boolean = false
}
@ -179,4 +183,14 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe
return textView
}
}
inner class Learned internal constructor(@StringRes var learned: Int) {
fun generate(context: Context): TextView {
val textView = TextView(context)
textView.setText(learned)
textView.setLinkTextColor(rh.gac(context, com.google.android.material.R.attr.colorSecondary))
return textView
}
}
}

View file

@ -39,11 +39,13 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R
return !virtualPumpPlugin.isEnabled()
}
})
tasks.add(object : Task(this, R.string.objectives_pumpstatusavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(info.nightscout.core.utils.R.string.key_objectives_pump_status_is_available_in_ns, false)
}
})
tasks.add(
object : Task(this, R.string.objectives_pumpstatusavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(info.nightscout.core.utils.R.string.key_objectives_pump_status_is_available_in_ns, false)
}
}.learned(Learned(R.string.objectives_0_learned))
)
tasks.add(object : Task(this, R.string.hasbgdata) {
override fun isCompleted(): Boolean {
return iobCobCalculator.ads.lastBg() != null

View file

@ -44,10 +44,13 @@ class Objective1 @Inject constructor(injector: HasAndroidInjector) : Objective(i
return sp.getBoolean(info.nightscout.core.utils.R.string.key_objectiveuseloop, false)
}
}.hint(Hint(R.string.useaction_hint)))
tasks.add(object : Task(this, R.string.objectives_usescale) {
override fun isCompleted(): Boolean {
return sp.getBoolean(info.nightscout.core.utils.R.string.key_objectiveusescale, false)
}
}.hint(Hint(R.string.usescale_hint)))
tasks.add(
object : Task(this, R.string.objectives_usescale) {
override fun isCompleted(): Boolean {
return sp.getBoolean(info.nightscout.core.utils.R.string.key_objectiveusescale, false)
}
}.hint(Hint(R.string.usescale_hint))
.learned(Learned(R.string.objectives_usage_learned))
)
}
}

View file

@ -7,6 +7,9 @@ import info.nightscout.shared.utils.T
class Objective10(injector: HasAndroidInjector) : Objective(injector, "auto", R.string.objectives_auto_objective, R.string.objectives_auto_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
tasks.add(
MinimumDurationTask(this, T.days(28).msecs())
.learned(Learned(R.string.objectives_autosens_learned))
)
}
}

View file

@ -7,216 +7,271 @@ import info.nightscout.plugins.constraints.R
class Objective2(injector: HasAndroidInjector) : Objective(injector, "exam", R.string.objectives_exam_objective, R.string.objectives_exam_gate) {
init {
tasks.add(ExamTask(this, R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(Option(R.string.prerequisites_nightscout, true))
.option(Option(R.string.prerequisites_computer, true))
.option(Option(R.string.prerequisites_pump, true))
.option(Option(R.string.prerequisites_beanandroiddeveloper, false))
.hint(Hint(R.string.prerequisites_hint1))
tasks.add(
ExamTask(this, R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(Option(R.string.prerequisites_nightscout, true))
.option(Option(R.string.prerequisites_computer, true))
.option(Option(R.string.prerequisites_pump, true))
.option(Option(R.string.prerequisites_beanandroiddeveloper, false))
.hint(Hint(R.string.prerequisites_hint1))
.learned(Learned(R.string.objectives_exam_learned_prerequisites))
)
tasks.add(ExamTask(this, R.string.prerequisites2_label, R.string.prerequisites2_what, "prerequisites2")
.option(Option(R.string.prerequisites2_profile, true))
.option(Option(R.string.prerequisites2_device, true))
.option(Option(R.string.prerequisites2_internet, false))
.option(Option(R.string.prerequisites2_supportedcgm, true))
.hint(Hint(R.string.prerequisites2_hint1))
tasks.add(
ExamTask(this, R.string.prerequisites2_label, R.string.prerequisites2_what, "prerequisites2")
.option(Option(R.string.prerequisites2_profile, true))
.option(Option(R.string.prerequisites2_device, true))
.option(Option(R.string.prerequisites2_internet, false))
.option(Option(R.string.prerequisites2_supportedcgm, true))
.hint(Hint(R.string.prerequisites2_hint1))
.learned(Learned(R.string.objectives_exam_learned_prerequisites2))
)
tasks.add(ExamTask(this, R.string.basaltest_label, R.string.basaltest_when, "basaltest")
.option(Option(R.string.basaltest_fixed, false))
.option(Option(R.string.basaltest_havingregularhighlow, true))
.option(Option(R.string.basaltest_weekly, false))
.option(Option(R.string.basaltest_beforeloop, true))
.hint(Hint(R.string.basaltest_hint1))
tasks.add(
ExamTask(this, R.string.basaltest_label, R.string.basaltest_when, "basaltest")
.option(Option(R.string.basaltest_fixed, false))
.option(Option(R.string.basaltest_havingregularhighlow, true))
.option(Option(R.string.basaltest_weekly, false))
.option(Option(R.string.basaltest_beforeloop, true))
.hint(Hint(R.string.basaltest_hint1))
.learned(Learned(R.string.objectives_exam_learned_basaltest))
)
tasks.add(ExamTask(this, R.string.dia_label_exam, R.string.dia_whatmeansdia, "dia")
.option(Option(R.string.dia_profile, true))
.option(Option(R.string.dia_minimumis5h, true))
.option(Option(R.string.dia_meaningisequaltodiapump, false))
.option(Option(R.string.dia_valuemustbedetermined, true))
.hint(Hint(R.string.dia_hint1))
tasks.add(
ExamTask(this, R.string.dia_label_exam, R.string.dia_whatmeansdia, "dia")
.option(Option(R.string.dia_profile, true))
.option(Option(R.string.dia_minimumis5h, true))
.option(Option(R.string.dia_meaningisequaltodiapump, false))
.option(Option(R.string.dia_valuemustbedetermined, true))
.hint(Hint(R.string.dia_hint1))
.learned(Learned(R.string.objectives_exam_learned_dia))
)
tasks.add(ExamTask(this, R.string.isf_label_exam, R.string.blank, "isf")
.option(Option(R.string.isf_decreasingvalue, true))
.option(Option(R.string.isf_preferences, false))
.option(Option(R.string.isf_increasingvalue, false))
.option(Option(R.string.isf_noeffect, false))
.hint(Hint(R.string.isf_hint1))
.hint(Hint(R.string.isf_hint2))
tasks.add(
ExamTask(this, R.string.isf_label_exam, R.string.blank, "isf")
.option(Option(R.string.isf_decreasingvalue, true))
.option(Option(R.string.isf_preferences, false))
.option(Option(R.string.isf_increasingvalue, false))
.option(Option(R.string.isf_noeffect, false))
.hint(Hint(R.string.isf_hint1))
.hint(Hint(R.string.isf_hint2))
.learned(Learned(R.string.objectives_exam_learned_isf))
)
tasks.add(ExamTask(this, R.string.ic_label_exam, R.string.blank, "ic")
.option(Option(R.string.ic_increasingvalue, true))
.option(Option(R.string.ic_decreasingvalue, false))
.option(Option(R.string.ic_multiple, true))
.option(Option(R.string.ic_isf, false))
.hint(Hint(R.string.ic_hint1))
tasks.add(
ExamTask(this, R.string.ic_label_exam, R.string.blank, "ic")
.option(Option(R.string.ic_increasingvalue, true))
.option(Option(R.string.ic_decreasingvalue, false))
.option(Option(R.string.ic_multiple, true))
.option(Option(R.string.ic_isf, false))
.hint(Hint(R.string.ic_hint1))
.learned(Learned(R.string.objectives_exam_learned_ic))
)
tasks.add(ExamTask(this, R.string.hypott_label, R.string.hypott_whenhypott, "hypott")
.option(Option(R.string.hypott_preventoversmb, true))
.option(Option(R.string.hypott_exercise, false))
.option(Option(R.string.hypott_wrongbasal, false))
.option(Option(R.string.hypott_0basal, false))
.hint(Hint(R.string.hypott_hint1))
tasks.add(
ExamTask(this, R.string.hypott_label, R.string.hypott_whenhypott, "hypott")
.option(Option(R.string.hypott_preventoversmb, true))
.option(Option(R.string.hypott_exercise, false))
.option(Option(R.string.hypott_wrongbasal, false))
.option(Option(R.string.hypott_0basal, false))
.hint(Hint(R.string.hypott_hint1))
.learned(Learned(R.string.objectives_exam_learned_hypott))
)
tasks.add(ExamTask(this, R.string.profileswitch_label, R.string.profileswitch_pctwillchange, "profileswitch")
.option(Option(R.string.profileswitch_basallower, true))
.option(Option(R.string.profileswitch_isfhigher, true))
.option(Option(R.string.profileswitch_iclower, false))
.option(Option(R.string.profileswitch_unchanged, false))
.hint(Hint(R.string.profileswitch_hint1))
tasks.add(
ExamTask(this, R.string.profileswitch_label, R.string.profileswitch_pctwillchange, "profileswitch")
.option(Option(R.string.profileswitch_basallower, true))
.option(Option(R.string.profileswitch_isfhigher, true))
.option(Option(R.string.profileswitch_iclower, false))
.option(Option(R.string.profileswitch_unchanged, false))
.hint(Hint(R.string.profileswitch_hint1))
.learned(Learned(R.string.objectives_exam_learned_profileswitch))
)
tasks.add(ExamTask(this, R.string.profileswitch2_label, R.string.profileswitch2_pctwillchange, "profileswitch2")
.option(Option(R.string.profileswitch2_bghigher, false))
.option(Option(R.string.profileswitch2_basalhigher, true))
.option(Option(R.string.profileswitch2_bgunchanged, true))
.option(Option(R.string.profileswitch2_isfhigher, false))
.hint(Hint(R.string.profileswitch_hint1))
tasks.add(
ExamTask(this, R.string.profileswitch2_label, R.string.profileswitch2_pctwillchange, "profileswitch2")
.option(Option(R.string.profileswitch2_bghigher, false))
.option(Option(R.string.profileswitch2_basalhigher, true))
.option(Option(R.string.profileswitch2_bgunchanged, true))
.option(Option(R.string.profileswitch2_isfhigher, false))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitchtime_label, R.string.profileswitchtime_iwant, "profileswitchtime")
.option(Option(R.string.profileswitchtime_2, false))
.option(Option(R.string.profileswitchtime__2, true))
.option(Option(R.string.profileswitchtime_tt, false))
.option(Option(R.string.profileswitchtime_100, false))
.hint(Hint(R.string.profileswitchtime_hint1))
tasks.add(
ExamTask(this, R.string.profileswitchtime_label, R.string.profileswitchtime_iwant, "profileswitchtime")
.option(Option(R.string.profileswitchtime_2, false))
.option(Option(R.string.profileswitchtime__2, true))
.option(Option(R.string.profileswitchtime_tt, false))
.option(Option(R.string.profileswitchtime_100, false))
.hint(Hint(R.string.profileswitchtime_hint1))
.learned(Learned(R.string.objectives_exam_learned_profileswitchtime))
)
tasks.add(ExamTask(this, R.string.profileswitch4_label, R.string.blank, "profileswitch4")
.option(Option(R.string.profileswitch4_rates, true))
.option(Option(R.string.profileswitch4_internet, true))
.option(Option(R.string.profileswitch4_sufficient, false))
.option(Option(R.string.profileswitch4_multi, true))
.hint(Hint(R.string.profileswitch_hint1))
tasks.add(
ExamTask(this, R.string.profileswitch4_label, R.string.blank, "profileswitch4")
.option(Option(R.string.profileswitch4_rates, true))
.option(Option(R.string.profileswitch4_internet, true))
.option(Option(R.string.profileswitch4_sufficient, false))
.option(Option(R.string.profileswitch4_multi, true))
.hint(Hint(R.string.profileswitch_hint1))
.learned(Learned(R.string.objectives_exam_learned_profileswitch4))
)
tasks.add(ExamTask(this, R.string.exerciseprofile_label, R.string.exerciseprofile_whattodo, "exercise")
.option(Option(R.string.exerciseprofile_switchprofileabove100, false))
.option(Option(R.string.exerciseprofile_switchprofilebelow100, true))
.option(Option(R.string.exerciseprofile_suspendloop, false))
.option(Option(R.string.exerciseprofile_leaveat100, false))
.hint(Hint(R.string.exerciseprofile_hint1))
tasks.add(
ExamTask(this, R.string.exerciseprofile_label, R.string.exerciseprofile_whattodo, "exercise")
.option(Option(R.string.exerciseprofile_switchprofileabove100, false))
.option(Option(R.string.exerciseprofile_switchprofilebelow100, true))
.option(Option(R.string.exerciseprofile_suspendloop, false))
.option(Option(R.string.exerciseprofile_leaveat100, false))
.hint(Hint(R.string.exerciseprofile_hint1))
.learned(Learned(R.string.objectives_exam_learned_exercise))
)
tasks.add(ExamTask(this, R.string.exercise_label, R.string.exercise_whattodo, "exercise2")
.option(Option(R.string.exercise_settt, true))
.option(Option(R.string.exercise_setfinished, false))
.option(Option(R.string.exercise_setunchanged, false))
.option(Option(R.string.exercise_15g, false))
.hint(Hint(R.string.exercise_hint1))
tasks.add(
ExamTask(this, R.string.exercise_label, R.string.exercise_whattodo, "exercise2")
.option(Option(R.string.exercise_settt, true))
.option(Option(R.string.exercise_setfinished, false))
.option(Option(R.string.exercise_setunchanged, false))
.option(Option(R.string.exercise_15g, false))
.hint(Hint(R.string.exercise_hint1))
.learned(Learned(R.string.objectives_exam_learned_exercise2))
)
tasks.add(ExamTask(this, R.string.noisycgm_label, R.string.noisycgm_whattodo, "noisycgm")
.option(Option(R.string.noisycgm_nothing, false))
.option(Option(R.string.noisycgm_pause, true))
.option(Option(R.string.noisycgm_replacesensor, true))
.option(Option(R.string.noisycgm_checksmoothing, true))
.hint(Hint(R.string.noisycgm_hint1))
tasks.add(
ExamTask(this, R.string.noisycgm_label, R.string.noisycgm_whattodo, "noisycgm")
.option(Option(R.string.noisycgm_nothing, false))
.option(Option(R.string.noisycgm_pause, true))
.option(Option(R.string.noisycgm_replacesensor, true))
.option(Option(R.string.noisycgm_checksmoothing, true))
.hint(Hint(R.string.noisycgm_hint1))
.learned(Learned(R.string.objectives_exam_learned_noisycgm))
)
tasks.add(ExamTask(this, R.string.pumpdisconnect_label, R.string.blank, "pumpdisconnect")
.option(Option(R.string.pumpdisconnect_unnecessary, false))
.option(Option(R.string.pumpdisconnect_missinginsulin, true))
.option(Option(R.string.pumpdisconnect_notstop, false))
.option(Option(R.string.pumpdisconnect_openloop, false))
.hint(Hint(R.string.pumpdisconnect_hint1))
tasks.add(
ExamTask(this, R.string.pumpdisconnect_label, R.string.blank, "pumpdisconnect")
.option(Option(R.string.pumpdisconnect_unnecessary, false))
.option(Option(R.string.pumpdisconnect_missinginsulin, true))
.option(Option(R.string.pumpdisconnect_notstop, false))
.option(Option(R.string.pumpdisconnect_openloop, false))
.hint(Hint(R.string.pumpdisconnect_hint1))
.learned(Learned(R.string.objectives_exam_learned_pumpdisconnect))
)
tasks.add(ExamTask(this, R.string.insulin_plugins, R.string.insulin_ultrarapid, "insulin")
.option(Option(R.string.insulin_novorapid, false))
.option(Option(R.string.insulin_humalog, false))
.option(Option(R.string.insulin_actrapid, false))
.option(Option(R.string.insulin_fiasp, true))
.hint(Hint(R.string.insulin_hint1))
tasks.add(
ExamTask(this, R.string.insulin_plugins, R.string.insulin_ultrarapid, "insulin")
.option(Option(R.string.insulin_novorapid, false))
.option(Option(R.string.insulin_humalog, false))
.option(Option(R.string.insulin_actrapid, false))
.option(Option(R.string.insulin_fiasp, true))
.hint(Hint(R.string.insulin_hint1))
.learned(Learned(R.string.objectives_exam_learned_insulin))
)
tasks.add(ExamTask(this, R.string.sensitivity_label, R.string.blank, "sensitivity")
.option(Option(R.string.sensitivity_adjust, true))
.option(Option(R.string.sensitivity_edit, false))
.option(Option(R.string.sensitivity_cannula, true))
.option(Option(R.string.sensitivity_time, true))
.hint(Hint(R.string.sensitivity_hint1))
.hint(Hint(R.string.sensitivity_hint2))
tasks.add(
ExamTask(this, R.string.sensitivity_label, R.string.blank, "sensitivity")
.option(Option(R.string.sensitivity_adjust, true))
.option(Option(R.string.sensitivity_edit, false))
.option(Option(R.string.sensitivity_cannula, true))
.option(Option(R.string.sensitivity_time, true))
.hint(Hint(R.string.sensitivity_hint1))
.hint(Hint(R.string.sensitivity_hint2))
.learned(Learned(R.string.objectives_exam_learned_sensitivity))
)
tasks.add(ExamTask(this, R.string.objectives_label, R.string.objectives_howtosave, "objectives")
.option(Option(R.string.objectives_notesettings, false))
.option(Option(R.string.objectives_afterobjective, true))
.option(Option(R.string.objectives_afterchange, true))
.option(Option(R.string.objectives_afterinitialsetup, true))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
tasks.add(
ExamTask(this, R.string.objectives_label, R.string.objectives_howtosave, "objectives")
.option(Option(R.string.objectives_notesettings, false))
.option(Option(R.string.objectives_afterobjective, true))
.option(Option(R.string.objectives_afterchange, true))
.option(Option(R.string.objectives_afterinitialsetup, true))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
.learned(Learned(R.string.objectives_exam_learned_objectives))
)
tasks.add(ExamTask(this, R.string.objectives2_label, R.string.objectives_howtosave, "objectives2")
.option(Option(R.string.objectives2_maintenance, true))
.option(Option(R.string.objectives2_internalstorage, true))
.option(Option(R.string.objectives2_cloud, true))
.option(Option(R.string.objectives2_easyrestore, false))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
tasks.add(
ExamTask(this, R.string.objectives2_label, R.string.objectives_howtosave, "objectives2")
.option(Option(R.string.objectives2_maintenance, true))
.option(Option(R.string.objectives2_internalstorage, true))
.option(Option(R.string.objectives2_cloud, true))
.option(Option(R.string.objectives2_easyrestore, false))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
.learned(Learned(R.string.objectives_exam_learned_objectives2))
)
tasks.add(ExamTask(this, R.string.update_label, R.string.blank, "update")
.option(Option(R.string.update_git, true))
.option(Option(R.string.update_askfriend, false))
.option(Option(R.string.update_keys, true))
.option(Option(R.string.update_asap, true))
.hint(Hint(R.string.update_hint1))
tasks.add(
ExamTask(this, R.string.update_label, R.string.blank, "update")
.option(Option(R.string.update_git, true))
.option(Option(R.string.update_askfriend, false))
.option(Option(R.string.update_keys, true))
.option(Option(R.string.update_asap, true))
.hint(Hint(R.string.update_hint1))
)
tasks.add(ExamTask(this, R.string.troubleshooting_label, R.string.troubleshooting_wheretoask, "troubleshooting")
.option(Option(R.string.troubleshooting_fb, true))
.option(Option(R.string.troubleshooting_wiki, true))
.option(Option(R.string.troubleshooting_gitter, true))
.option(Option(R.string.troubleshooting_yourendo, false))
.hint(Hint(R.string.troubleshooting_hint1))
.hint(Hint(R.string.troubleshooting_hint2))
.hint(Hint(R.string.troubleshooting_hint3))
tasks.add(
ExamTask(this, R.string.troubleshooting_label, R.string.troubleshooting_wheretoask, "troubleshooting")
.option(Option(R.string.troubleshooting_fb, true))
.option(Option(R.string.troubleshooting_wiki, true))
.option(Option(R.string.troubleshooting_gitter, true))
.option(Option(R.string.troubleshooting_yourendo, false))
.hint(Hint(R.string.troubleshooting_hint1))
.hint(Hint(R.string.troubleshooting_hint2))
.hint(Hint(R.string.troubleshooting_hint3))
)
tasks.add(ExamTask(this, R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo, "wrongcarbs")
.option(Option(R.string.wrongcarbs_addinsulin, false))
.option(Option(R.string.wrongcarbs_treatmentstab, true))
.option(Option(R.string.wrongcarbs_donothing, false))
.option(Option(R.string.wrongcarbs_bolus, false))
tasks.add(
ExamTask(this, R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo, "wrongcarbs")
.option(Option(R.string.wrongcarbs_addinsulin, false))
.option(Option(R.string.wrongcarbs_treatmentstab, true))
.option(Option(R.string.wrongcarbs_donothing, false))
.option(Option(R.string.wrongcarbs_bolus, false))
.learned(Learned(R.string.objectives_exam_learned_wrongcarbs))
)
tasks.add(ExamTask(this, R.string.wronginsulin_label, R.string.wronginsulin_whattodo, "wronginsulin")
.option(Option(R.string.wronginsulin_careportal, false))
.option(Option(R.string.wronginsulin_compare, true))
.option(Option(R.string.wronginsulin_prime, true))
.option(Option(R.string.wrongcarbs_donothing, false))
tasks.add(
ExamTask(this, R.string.wronginsulin_label, R.string.wronginsulin_whattodo, "wronginsulin")
.option(Option(R.string.wronginsulin_careportal, false))
.option(Option(R.string.wronginsulin_compare, true))
.option(Option(R.string.wronginsulin_prime, true))
.option(Option(R.string.wrongcarbs_donothing, false))
)
tasks.add(ExamTask(this, info.nightscout.core.ui.R.string.iob_label, R.string.blank, "iob")
.option(Option(R.string.iob_value, true))
.option(Option(R.string.iob_hightemp, false))
.option(Option(R.string.iob_negiob, true))
.option(Option(R.string.iob_posiob, true))
tasks.add(
ExamTask(this, info.nightscout.core.ui.R.string.iob_label, R.string.blank, "iob")
.option(Option(R.string.iob_value, true))
.option(Option(R.string.iob_hightemp, false))
.option(Option(R.string.iob_negiob, true))
.option(Option(R.string.iob_posiob, true))
.learned(Learned(R.string.objectives_exam_learned_iob))
)
tasks.add(ExamTask(this, R.string.cob_label, R.string.cob_question, "cob1")
.option(Option(R.string.cob_longer, true))
.option(Option(R.string.cob_shorter, false))
.option(Option(R.string.cob_no_effect, false))
tasks.add(
ExamTask(this, R.string.cob_label, R.string.cob_question, "cob1")
.option(Option(R.string.cob_longer, true))
.option(Option(R.string.cob_shorter, false))
.option(Option(R.string.cob_no_effect, false))
)
tasks.add(ExamTask(this, R.string.cob_label, R.string.cob2_question, "cob2")
.option(Option(R.string.cob2_longer, false))
.option(Option(R.string.cob2_shorter, true))
.option(Option(R.string.cob2_no_effect, false))
tasks.add(
ExamTask(this, R.string.cob_label, R.string.cob2_question, "cob2")
.option(Option(R.string.cob2_longer, false))
.option(Option(R.string.cob2_shorter, true))
.option(Option(R.string.cob2_no_effect, false))
)
tasks.add(ExamTask(this, R.string.cob_label, R.string.cob3_question, "cob3")
.option(Option(R.string.cob3_longer, false))
.option(Option(R.string.cob3_shorter, false))
.option(Option(R.string.cob3_no_effect, true))
tasks.add(
ExamTask(this, R.string.cob_label, R.string.cob3_question, "cob3")
.option(Option(R.string.cob3_longer, false))
.option(Option(R.string.cob3_shorter, false))
.option(Option(R.string.cob3_no_effect, true))
.learned(Learned(R.string.objectives_exam_learned_cob))
)
tasks.add(ExamTask(this, R.string.breadgrams_label, R.string.blank, "breadgrams")
.option(Option(R.string.breadgrams_grams, true))
.option(Option(R.string.breadgrams_exchange, false))
.option(Option(R.string.breadgrams_decay, true))
.option(Option(R.string.breadgrams_calc, true))
.hint(Hint(R.string.breadgrams_hint1))
tasks.add(
ExamTask(this, R.string.breadgrams_label, R.string.blank, "breadgrams")
.option(Option(R.string.breadgrams_grams, true))
.option(Option(R.string.breadgrams_exchange, false))
.option(Option(R.string.breadgrams_decay, true))
.option(Option(R.string.breadgrams_calc, true))
.hint(Hint(R.string.breadgrams_hint1))
.learned(Learned(R.string.objectives_exam_learned_breadgrams))
)
tasks.add(ExamTask(this, R.string.extendedcarbs_label, R.string.extendedcarbs_handling, "extendedcarbs")
.option(Option(R.string.extendedcarbs_future, true))
.option(Option(R.string.extendedcarbs_free, false))
.option(Option(R.string.extendedcarbs_fat, true))
.option(Option(R.string.extendedcarbs_rescue, false))
.hint(Hint(R.string.extendedcarbs_hint1))
tasks.add(
ExamTask(this, R.string.extendedcarbs_label, R.string.extendedcarbs_handling, "extendedcarbs")
.option(Option(R.string.extendedcarbs_future, true))
.option(Option(R.string.extendedcarbs_free, false))
.option(Option(R.string.extendedcarbs_fat, true))
.option(Option(R.string.extendedcarbs_rescue, false))
.hint(Hint(R.string.extendedcarbs_hint1))
.learned(Learned(R.string.objectives_exam_learned_ecarbs))
)
tasks.add(ExamTask(this, R.string.nsclient_label, R.string.nsclient_howcanyou, "nsclient")
.option(Option(R.string.nsclient_nightscout, true))
.option(Option(R.string.nsclient_dexcomfollow, true))
.option(Option(R.string.nsclient_data, true))
.option(Option(R.string.nsclient_fullcontrol, false))
.hint(Hint(R.string.nsclient_hint1))
tasks.add(
ExamTask(this, R.string.nsclient_label, R.string.nsclient_howcanyou, "nsclient")
.option(Option(R.string.nsclient_nightscout, true))
.option(Option(R.string.nsclient_dexcomfollow, true))
.option(Option(R.string.nsclient_data, true))
.option(Option(R.string.nsclient_fullcontrol, false))
.hint(Hint(R.string.nsclient_hint1))
.learned(Learned(R.string.objectives_exam_learned_nsclient))
)
tasks.add(ExamTask(this, R.string.other_medication_label, R.string.other_medication_text, "otherMedicationWarning")
.option(Option(info.nightscout.core.ui.R.string.yes, true))
.option(Option(info.nightscout.core.ui.R.string.no, false))
tasks.add(
ExamTask(this, R.string.other_medication_label, R.string.other_medication_text, "otherMedicationWarning")
.option(Option(info.nightscout.core.ui.R.string.yes, true))
.option(Option(info.nightscout.core.ui.R.string.no, false))
)
for (task in tasks) (task as ExamTask).options.shuffle()

View file

@ -13,15 +13,19 @@ class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(i
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
tasks.add(object : Task(this, R.string.objectives_manualenacts) {
override fun isCompleted(): Boolean {
return sp.getInt(info.nightscout.core.utils.R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED
}
tasks.add(
object : Task(this, R.string.objectives_manualenacts) {
override fun isCompleted(): Boolean {
return sp.getInt(info.nightscout.core.utils.R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED
}
override val progress: String
get() = if (sp.getInt(info.nightscout.core.utils.R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED) rh.gs(R.string.completed_well_done) else sp.getInt(info.nightscout.core.utils.R.string.key_ObjectivesmanualEnacts, 0)
.toString() + " / " + MANUAL_ENACTS_NEEDED
})
override val progress: String
get() =
if (sp.getInt(info.nightscout.core.utils.R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED)
rh.gs(R.string.completed_well_done)
else sp.getInt(info.nightscout.core.utils.R.string.key_ObjectivesmanualEnacts, 0).toString() + " / " + MANUAL_ENACTS_NEEDED
}.learned(Learned(R.string.objectives_openloop_learned))
)
}
companion object {

View file

@ -1,7 +1,29 @@
package info.nightscout.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.plugins.constraints.R
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective4(injector: HasAndroidInjector) : Objective(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate)
class Objective4(injector: HasAndroidInjector) : Objective(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate) {
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activePlugin: ActivePlugin
init {
tasks.add(
object : Task(this, R.string.objectives_maxbasal_gate) {
override fun isCompleted(): Boolean {
val profile = profileFunction.getProfile() ?: return false
val maxBasalSet = (activePlugin.activeAPS as Constraints).applyBasalConstraints(Constraint(Constants.REALLYHIGHBASALRATE), profile)
val maxDailyBasal = profile.getMaxDailyBasal()
return maxBasalSet.value() > 2.8 * maxDailyBasal
}
}.learned(Learned(R.string.objectives_maxbasal_learned))
)
}
}

View file

@ -14,12 +14,14 @@ class Objective5(injector: HasAndroidInjector) : Objective(injector, "maxiobzero
init {
tasks.add(MinimumDurationTask(this, T.days(5).msecs()))
tasks.add(object : Task(this, R.string.closedmodeenabled) {
override fun isCompleted(): Boolean {
val closedLoopEnabled = Constraint(true)
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled)
return closedLoopEnabled.value()
}
})
tasks.add(
object : Task(this, R.string.closedmodeenabled) {
override fun isCompleted(): Boolean {
val closedLoopEnabled = Constraint(true)
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled)
return closedLoopEnabled.value()
}
}.learned(Learned(R.string.objectives_maxiobzero_learned))
)
}
}

View file

@ -1,6 +1,7 @@
package info.nightscout.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.ApsMode
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.plugins.constraints.R
import info.nightscout.shared.utils.T
@ -15,7 +16,7 @@ class Objective6(injector: HasAndroidInjector) : Objective(injector, "maxiob", R
tasks.add(MinimumDurationTask(this, T.days(1).msecs()))
tasks.add(
object : Task(this, R.string.closedmodeenabled) {
override fun isCompleted(): Boolean = sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open") == "closed"
override fun isCompleted(): Boolean = ApsMode.fromString(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)) == ApsMode.CLOSED
})
tasks.add(
object : Task(this, R.string.maxiobset) {
@ -24,6 +25,7 @@ class Objective6(injector: HasAndroidInjector) : Objective(injector, "maxiob", R
val maxIOB = constraintChecker.getMaxIOBAllowed().value()
return maxIOB > 0
}
})
}.learned(Learned(R.string.objectives_maxiob_learned))
)
}
}

View file

@ -7,6 +7,9 @@ import info.nightscout.shared.utils.T
class Objective7(injector: HasAndroidInjector) : Objective(injector, "autosens", R.string.objectives_autosens_objective, R.string.objectives_autosens_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
tasks.add(
MinimumDurationTask(this, T.days(7).msecs())
.learned(Learned(R.string.objectives_autosens_learned))
)
}
}

View file

@ -7,6 +7,9 @@ import info.nightscout.shared.utils.T
class Objective9(injector: HasAndroidInjector) : Objective(injector, "smb", R.string.objectives_smb_objective, R.string.objectives_smb_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
tasks.add(
MinimumDurationTask(this, T.days(28).msecs())
.learned(Learned(R.string.objectives_smb_learned))
)
}
}

View file

@ -8,6 +8,7 @@ import info.nightscout.core.utils.extensions.storeDouble
import info.nightscout.core.utils.extensions.storeInt
import info.nightscout.core.utils.extensions.storeString
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.ApsMode
import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.constraints.Safety
@ -67,8 +68,8 @@ class SafetyPlugin @Inject constructor(
}
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
val mode = sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")
if (mode == "open") value.set(aapsLogger, false, rh.gs(R.string.closedmodedisabledinpreferences), this)
val mode = ApsMode.fromString(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name))
if (mode == ApsMode.OPEN) value.set(aapsLogger, false, rh.gs(R.string.closedmodedisabledinpreferences), this)
if (!config.isEngineeringModeOrRelease()) {
if (value.value()) {
uiInteraction.addNotification(Notification.TOAST_ALARM, rh.gs(R.string.closed_loop_disabled_on_dev_branch), Notification.NORMAL)
@ -159,8 +160,8 @@ class SafetyPlugin @Inject constructor(
}
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> {
val apsMode = sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")
if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, rh.gs(info.nightscout.core.ui.R.string.limiting_iob, HardLimits.MAX_IOB_LGS, rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)), this)
val apsMode = ApsMode.fromString(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name))
if (apsMode == ApsMode.LGS) maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, rh.gs(info.nightscout.core.ui.R.string.limiting_iob, HardLimits.MAX_IOB_LGS, rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)), this)
return maxIob
}

View file

@ -85,42 +85,6 @@
android:layout_gravity="end"
android:text="@string/objectives_button_unstart" />
<TextView
android:id="@+id/inputhint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/enter_code_obtained_from_developers_to_bypass_the_rest_of_objectives" />
<TextView
android:id="@+id/requestcode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Request code: XXXXX" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<EditText
android:id="@+id/input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:inputType="text"
tools:hint="XXXXXXXXXX"
android:importantForAutofill="no" />
<com.google.android.material.button.MaterialButton
android:id="@+id/enterbutton"
style="@style/OkCancelButton.Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/objectives_button_enter" />
</LinearLayout>
<TextView
android:id="@+id/accomplished"
android:layout_width="wrap_content"
@ -128,6 +92,23 @@
android:layout_marginTop="8dp"
tools:text="Accomplished" />
<TextView
android:id="@+id/learned_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:text="@string/what_i_ve_learned"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/learned"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View file

@ -4,61 +4,138 @@
<string name="dia_label_exam">Duración de la acción de insulina (DAI)</string>
<string name="dia_profile">Debes establecer el valor de DAI en tu perfil.</string>
<string name="dia_minimumis5h">El valor mínimo permitido es de 5 horas.</string>
<string name="dia_hint1">https://androidaps.readthedocs.io/en/latest/EN/Configuration/Config-Builder.html?#insulin</string>
<string name="dia_meaningisequaltodiapump">Si estás satisfecho con el valor de DAI que utilizas en tu bomba antes de utilizar AAPS y te funcionó bien, no hay necesidad de cambiarlo cuando empieces a usar el bucle cerrado.</string>
<string name="dia_valuemustbedetermined">Deberás determinar por ti mismo el valor apropiado para DAI.</string>
<string name="hypott_label">Objetivo temporal ante Hipoglucemia</string>
<string name="hypott_whenhypott">¿Cuál es la razón principal para establecer un objetivo temporal por hipoglucemia?</string>
<string name="hypott_wrongbasal">Para corregir hipoglucemias causadas por ajustes incorrectos de la tasa basal.</string>
<string name="hypott_preventoversmb">Para evitar que AAPS corrija de forma excesiva ante un rápido aumento de la glucosa por consumir carbohidratos simples para recuperarse de una hipoglucemia.</string>
<string name="hypott_exercise">Para corregir una hipoglucemia que sucedió como resultando del ejercicio.</string>
<string name="hypott_0basal">Para evitar que la glucosa sanguínea continue bajando si ya hay una tasa basal temporal igual a 0% actividad.</string>
<string name="hypott_hint1">https://androidaps.readthedocs.io/es/latest/Usage/temptarget.html</string>
<string name="offlineprofile_whatprofile">¿Qué perfil puede ser usado y configurado estando sin conexión de Internet?</string>
<string name="offlineprofile_label">Tema: Perfil Local</string>
<string name="offlineprofile_nsprofile">El perfil que proviene de Nightscout (NS) puede ser utilizado, pero no configurado.</string>
<string name="offlineprofile_hint1">https://androidaps.readthedocs.io/es/latest/Configuration/Config-Builder.html#profile</string>
<string name="pumpdisconnect_label">Razones para seleccionar \"Desconectar bomba\" en AAPS</string>
<string name="pumpdisconnect_whattodo">¿Qué se debe hacer al desconectar la bomba de insulina?</string>
<string name="pumpdisconnect_unnecessary">Esto es innecesario, ya que no se entregará insulina si la bomba está físicamente desconectada.</string>
<string name="pumpdisconnect_missinginsulin">Evita que AAPS considere la insulina que no se suministró cuando la bomba estaba físicamente desconectada</string>
<string name="pumpdisconnect_notstop">Si la bomba permanece conectada, no se detendrá el suministro de insulina.</string>
<string name="pumpdisconnect_openloop">Se activará el modo de bucle abierto en AAPS.</string>
<string name="pumpdisconnect_hint1">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#other-settings</string>
<string name="objectives_label">Ajustes de AAPS</string>
<string name="objectives2_label">Ajustes de AAPS</string>
<string name="objectives_howtosave">¿Cuáles son las mejores prácticas para hacer copias de seguridad de sus configuraciones?</string>
<string name="objectives_notesettings">No necesita exportar sus configuraciones siempre que haga las tenga anotadas.</string>
<string name="objectives_afterobjective">Exportar tu configuración después de completar un objetivo.</string>
<string name="objectives_afterchange">Exportar las configuraciones después de cambiar cualquiera de sus ajustes.</string>
<string name="objectives_afterinitialsetup">Exportar las configuraciones una vez finalizada la configuración inicial y haber establecido sus preferencias.</string>
<string name="objectives2_maintenance">Exportar la configuración localmente usando el menú de mantenimiento.</string>
<string name="objectives2_internalstorage">El archivo de configuración se encuentra en la carpeta Almacenamiento/AAPS/preferencias en el teléfono.</string>
<string name="objectives2_cloud">Copie el archivo de preferencias a una ubicación segura fuera de su teléfono (p.e. mediante el uso del almacenamiento en la nube, conectando su teléfono con un cable a una computadora, correo electrónico, etc.)</string>
<string name="objectives2_easyrestore">Si su teléfono es dañado o perdido, hay formas fáciles de recuperar remotamente su configuración sin hacer una copia de seguridad.</string>
<string name="objectives_hint1">https://androidaps.readthedocs.io/es/latest/Usage/ExportImportSettings.html</string>
<string name="objectives_hint2">https://androidaps.readthedocs.io/es/latest/Getting-Started/FAQ.html#what-emergency-equipment-is-recommended-to-take-with-me</string>
<string name="noisycgm_label">Lecturas CGM con mucha variabilidad</string>
<string name="noisycgm_whattodo">¿Qué se debe hacer si los datos de CGM tienen mucha variabilidad?</string>
<string name="noisycgm_nothing">No hacer nada: AAPS se ocupará de ello.</string>
<string name="noisycgm_pause">Deshabilitar el bucle cerrado para evitar posibles sobredosis o subdosis.</string>
<string name="noisycgm_replacesensor">Sustituya el sensor con valores muy variables o inexactos.</string>
<string name="noisycgm_checksmoothing">Comprueba que tu aplicación del CGM proporciona datos suavizados.</string>
<string name="noisycgm_hint1">https://androidaps.readthedocs.io/es/latest/Usage/Smoothing-Blood-Glucose-Data-in-xDrip.html#smoothing-blood-glucose-data</string>
<string name="exerciseprofile_label">Ejercicio y perfiles</string>
<string name="exerciseprofile_whattodo">¿Cómo puede usar los perfiles para mejorar el sistema durante el ejercicio aeróbico?</string>
<string name="exerciseprofile_switchprofilebelow100">Cambie el perfil por un valor menor a 100%.</string>
<string name="exerciseprofile_switchprofileabove100">Cambie el perfil por un valor mayor a 100%.</string>
<string name="exerciseprofile_leaveat100">Dejar el perfil configurado al 100%.</string>
<string name="exerciseprofile_suspendloop">Suspender el bucle.</string>
<string name="exerciseprofile_hint1">https://androidaps.readthedocs.io/es/latest/Usage/temptarget.html#activity-temp-target</string>
<string name="exercise_label">Ejercicios y objetivos temporales</string>
<string name="exercise_whattodo">¿Cómo se puede usar el objetivo temporal de glucosa para ayudar al sistema durante los ejercicios aeróbicos?</string>
<string name="exercise_settt">Establezca el objetivo de tipo actividad antes de comenzar el ejercicio, considerando un tiempo razonable antes de la actividad.</string>
<string name="exercise_setfinished">Establezca un objetivo de tipo actividad después de finalizar el ejercicio.</string>
<string name="exercise_setunchanged">Deja tu objetivo de glucosa sin cambios.</string>
<string name="exercise_15g">Espere hasta que los valores de glucosa se encuentren por debajo del valor definido en el objetivo de tipo hipoglicemia. Luego consuma 15 g de carbohidratos de rápida absorción.</string>
<string name="exercise_hint1">https://androidaps.readthedocs.io/es/latest/Usage/temptarget.html#activity-temp-target</string>
<string name="suspendloop_doigetinsulin">¿Puedo recibir insulina cuando el bucle está desactivado/suspendido?</string>
<string name="suspendloop_yes">Sí, la insulina basal sigue siendo suministrada.</string>
<string name="suspendloop_no">No, la administración de la insulina se detiene.</string>
<string name="basaltest_label">Pruebas para basales, I:C y FSI</string>
<string name="basaltest_when">¿Cada cuándo se deben validar estos valores?</string>
<string name="basaltest_beforeloop">Antes de empezar cualquier bucle.</string>
<string name="basaltest_havingregularhighlow">Cuando los valores de glucosa son muy altos o bajos de manera frecuente.</string>
<string name="basaltest_weekly">Al menos una vez a la semana.</string>
<string name="basaltest_fixed">Una vez establecidos y validados, estos valores no deberían cambiar a lo largo del tiempo.</string>
<string name="basaltest_hint1">https://androidaps.readthedocs.io/es/latest/Getting-Started/FAQ.html#androidaps-settings</string>
<string name="prerequisites_label">Requisitos previos</string>
<string name="prerequisites_what">¿Qué es esencial para configurar y utilizar AAPS?</string>
<string name="prerequisites_determinedcorrectprofile">Información de perfil previamente validada (Basal, IC, FSI, DAI).</string>
<string name="prerequisites_computer">Una computadora con Android Studio instalado y configurado.</string>
<string name="prerequisites_phone">Un teléfono compatible.</string>
<string name="prerequisites_pump">Una bomba de insulina compatible, si planeas usar el sistema en modo bucle cerrado.</string>
<string name="prerequisites_nightscout">Nightscout, para tener un registro de los datos y revisar los parámetros de configuración.</string>
<string name="prerequisites_tidepoolaccount">Una cuenta de Tidepool.</string>
<string name="prerequisites_googleaccount">Una cuenta de Google.</string>
<string name="prerequisites_githubaccount">Una cuenta de Github.</string>
<string name="prerequisites_beanandroiddeveloper">Experiencia programando o editando código.</string>
<string name="prerequisites_own670g">Una bomba MiniMed 670G.</string>
<string name="prerequisites_hint1">https://androidaps.readthedocs.io/es/latest/Module/module.html</string>
<string name="prerequisites_smartwatch">Un Smartwatch.</string>
<string name="prerequisites_supportedcgm">Un MCG soportado.</string>
<string name="prerequisites2_label">Requisitos previos</string>
<string name="prerequisites2_what">¿Qué es esencial para configurar y utilizar AAPS?</string>
<string name="prerequisites2_profile">Parámetros validados para poder configurar un perfil (FSI, I:C, perfil basal, DAI etc.).</string>
<string name="prerequisites2_device">Un dispositivo Android compatible (e.j. un móvil, un smartwatch Android compatible o una tablet).</string>
<string name="prerequisites2_internet">AAPS requiere una conexión a Internet para funcionar con el modo bucle cerrado.</string>
<string name="prerequisites2_supportedcgm">Un medidor continuo de glucosa (MCG) y una aplicación capaz de recibir los valores proporcionados por el medidor en el móvil o tablet.</string>
<string name="prerequisites2_hint1">https://androidaps.readthedocs.io/en/latest/EN/Module/module.html</string>
<string name="update_label">Actualizando AAPS</string>
<string name="whatistrue">Seleccione todas las respuestas correctas.</string>
<string name="update_git">Es necesario tener Git instalado y configurado en la computadora.</string>
<string name="update_asap">Cuando esté disponible una versión más reciente de AAPS, las funciones de las versiones anteriores pueden ser limitadas de forma remota después de una fecha especifica.</string>
<string name="update_keys">Se debe guardar en un lugar seguro de su computadora la \"keystore\" (archivo.jks) que se ha empleado previamente y usar la misma \"keystore\" para futuras actualizaciones.</string>
<string name="update_neverupdate">Nunca se debe actualizar AAPS si el sistema está funcionando bien.</string>
<string name="update_askfriend">Si tienes problemas creando la aplicación (. apk), puedes instalar el mismo archivo. apk compilado por un amigo.</string>
<string name="update_hint1">https://androidaps.readthedocs.io/en/latest/EN/Installing-AndroidAPS/Update-to-new-version.html#update-to-a-new-version-or-branch</string>
<string name="troubleshooting_label">Solucionando problemas</string>
<string name="troubleshooting_wheretoask">¿Dónde puedes buscar ayuda con AAPS?</string>
<string name="troubleshooting_fb">Puede solicitar asesoramiento en el grupo de usuarios de AAPS en Facebook.</string>
<string name="troubleshooting_wiki">Deberías leer (y volver a leer) la documentación de AAPS.</string>
<string name="troubleshooting_gitter">Puedes solicitar asesoramiento y registrar problemas técnicos o incidencias en el grupo de Discord de AAPS.</string>
<string name="troubleshooting_yourendo">Debes preguntar a tu endocrinólogo o educador en diabetes.</string>
<string name="troubleshooting_hint1">https://androidaps.readthedocs.io/en/latest/EN/Installing-AndroidAPS/Update-to-new-version.html#troubleshooting</string>
<string name="troubleshooting_hint2">https://www.facebook.com/groups/AndroidAPSUsers/</string>
<string name="troubleshooting_hint3">https://discord.gg/4fQUWHZ4Mw</string>
<string name="insulin_plugins">Plugins de insulina</string>
<string name="insulin_ultrarapid">¿Qué insulina debes usar con el plugin Ultra-Rapid Oref?</string>
<string name="insulin_fiasp">Fiasp®</string>
<string name="insulin_novorapid">NovoRapid®/Novolog®</string>
<string name="insulin_humalog">Humalog®</string>
<string name="insulin_actrapid">Actrapid®/Humalin R®/\"insulina humana estándar\".</string>
<string name="insulin_hint1">https://androidaps.readthedocs.io/en/latest/EN/Configuration/Config-Builder.html#insulin</string>
<string name="sensitivity_label">Plugins de sensibilidad</string>
<string name="sensitivity_which">Seleccione todas las respuestas correctas.</string>
<string name="sensitivity_adjust">Los plugins de sensibilidad permiten a AAPS ajustarse para cambios temporales o de corta duración en la sensibilidad a la insulina (por ejemplo, cambios hormonales o problemas con la absorción en el sitio de infusión).</string>
<string name="sensitivity_edit">Los plugins de sensibilidad sugieren al usuario cambios en la cantidad de insulina basal a suministrar, en el factor de sensibilidad a la insulina (ISF) y en el ratio I:C y pueden ser incorporados al perfil definido.</string>
<string name="sensitivity_cannula">Registrar el cámbio de cánula reinicia Autosens, volviendo de nuevo al 100%.</string>
<string name="sensitivity_time">Algunas de las opciones del plugin tienen rangos de tiempo configurables que pueden ser definidos por el usuario.</string>
<string name="sensitivity_hint1">https://androidaps.readthedocs.io/en/latest/EN/Configuration/Sensitivity-detection-and-COB.html</string>
<string name="sensitivity_hint2">https://androidaps.readthedocs.io/es/latest/Usage/Open-APS-features.html?highlight=Autosens#autosens</string>
<string name="wrongcarbs_label">Error de ingreso de Carbohidratos</string>
<string name="wrongcarbs_whattodo">¿Qué deberías hacer si has cometido un error al ingresar carbohidratos?</string>
<string name="wrongcarbs_treatmentstab">Elimina la entrada incorrecta en pestaña de tratamientos e introduce el valor correcto.</string>
<string name="wrongcarbs_addinsulin">Darse un bolo desde el menú de llenado de la infusión.</string>
<string name="wrongcarbs_donothing">No hacer nada - AAPS realizará los ajustes necesarios.</string>
<string name="wrongcarbs_bolus">Darse un bolo usando el botón de Insulina (bolus) en página general.</string>
<string name="wronginsulin_label">Errores de entrega/entrada de insulina</string>
<string name="wronginsulin_whattodo">¿Qué debes hacer si recibiste menos insulina de la que sugiere la historia de la bomba p.ej. debido a una oclusión, una cánula fallida o olvidarse de reponer la bomba después de una ducha?</string>
<string name="wronginsulin_careportal">Elimina los valores de insulina del portal de Nightscout o Careportal para eliminarlos del historial de la bomba.</string>
<string name="wronginsulin_compare">Comparar valores en AAPS con el historial de la bomba (si la bomba los registra).</string>
<string name="wronginsulin_prime">Bolo una proporción de la insulina calculada “perdida” por jeringa/pluma o usando menú de Insulina.</string>
<string name="wronginsulin_donothing">No hacer nada y permite que AAPS corrija cualquier resultado de nivel alto de glucosa.</string>
<string name="cob_label">Carbohidratos activos (COB)</string>
<string name="cob_question">¿Cómo afecta el cambio del valor FSI al cálculo de COB?</string>
<string name="cob_longer">Incrementar el FSI hará que los carbohidratos se absorban más lentamente</string>
<string name="cob_shorter">Incrementar el FSI hará que los carbohidratos se absorban más rápidamente</string>

View file

@ -9,26 +9,35 @@
<string name="objectivenotfinished">Objective %1$d not finished</string>
<string name="objectives_0_objective">Setting up visualization and monitoring, and analyzing basals and ratios</string>
<string name="objectives_0_gate">Verify that BG is available in Nightscout, and pump insulin data is being uploaded</string>
<string name="objectives_0_learned">You made basic setup of AAPS ecosystem. Nightscout is not necessary for AAPS to run but it\'s very useful for reporting or monitoring of other patients. It\'s not necessary to be connected to NS all the time if you use NS only for yourself. You can setup to upload for example only on home wifi and save battery.</string>
<string name="objectives_openloop_objective">Starting on an open loop</string>
<string name="objectives_openloop_gate">Run in Open Loop mode for a few days and manually enact lots of temp basals. Set up and use temporary and default temporary targets (e.g. for activity or hypo treatment carbs)</string>
<string name="objectives_openloop_learned">Open loop can be used for recommendations if you don\'t have compatible pump or you are not ready to close the loop.</string>
<string name="objectives_maxbasal_objective">Understanding your open loop, including its temp basal recommendations</string>
<string name="objectives_maxbasal_gate">Based on that experience, decide what max basal should be, and set it on the pump and preferences</string>
<string name="objectives_maxbasal_learned">Take care about safety features and adjust safety parameters when necessary.</string>
<string name="objectives_maxiobzero_objective">Starting to close the loop with Low Glucose Suspend</string>
<string name="objectives_maxiobzero_gate">Run in closed loop with max IOB = 0 for a few days without too many LGS events</string>
<string name="objectives_maxiobzero_learned">Setting MaxIOB to zero prevents you from hypo and will not add more insulin above basal rate (except situation with negative IOB)</string>
<string name="objectives_maxiob_objective">Tuning the closed loop, raising max IOB above 0 and gradually lowering BG targets</string>
<string name="objectives_maxiob_gate">Run for a few days, and at least one night with no low BG alarms, before dropping BG</string>
<string name="objectives_maxiob_learned">Update MaxIOB as a child grows. Do not allow to the system give you more insulin than you can cover by food = really high value is a bad idea.</string>
<string name="objectives_autosens_objective">Adjust basals and ratios if needed, and then enable auto-sens</string>
<string name="objectives_autosens_gate">1 week successful daytime looping with regular carb entry</string>
<string name="objectives_autosens_learned">If your autosens result is not oscillating around 100% your profile probably wrong.</string>
<string name="objectives_smb_objective">Enabling additional features for daytime use, such as SMB</string>
<string name="objectives_auto_objective">Enabling automation</string>
<string name="objectives_smb_gate">You must read the wiki and rise maxIOB to get SMBs working fine! A good start is maxIOB=average mealbolus + 3 x max daily basal</string>
<string name="objectives_smb_learned">Using SMB is your goal. Oref1 algorithm was designed to help you with your boluses as well. You should not give full bolus for your food but only part of it and let AAPS give you the rest if needed. This way you have more space for miscalculated carbs. Did you know that you can set a percentage of bolus calculator result to reduce the size of bolus?</string>
<string name="objectives_auto_objective">Enabling automation</string>
<string name="objectives_auto_gate">Read the docs on how automation works. Set up your first simple rules. Instead of action let AAPS display only notification. When you are sure automation is triggered at the right time replace notification by real action. (https://androidaps.readthedocs.io/en/latest/EN/Usage/Automation.html)</string>
<string name="objectives_auto_learned">Automation can be a good servant but a bad master. Don\'t overuse it. Do not try to replace underlying algorithm. Test the rule with message only before use. It depends on order.</string>
<string name="objectives_bgavailableinns">BG available in NS</string>
<string name="objectives_pumpstatusavailableinns">Pump status available in NS</string>
<string name="objectives_manualenacts">Manual enacts</string>
<string name="accomplished">Accomplished: %1$s</string>
<string name="objectives_usage_objective">Learn how to control AAPS</string>
<string name="objectives_usage_gate">Perform different actions in AAPS</string>
<string name="objectives_usage_learned">You learned how to handle basics of AAPS. Main controls are on Overview screen accessible on click or long click, more controls is on Action screen. You should know how to put a screen of plugin to top scrollable menu or let it show in top-left list of enabled plugins.</string>
<string name="objectives_useprofileswitch">Set profile 90% for 10 min (Long-press profile name on Overview)</string>
<string name="objectives_usedisconnectpump">Simulate shower. Disconnect pump for 1h (Long-press on Open Loop)</string>
<string name="objectives_usereconnectpump">... and reconnect back the same way</string>
@ -36,12 +45,32 @@
<string name="objectives_useactions">In Config Builder enable Actions plugin, make it visible and display its content from top menu</string>
<string name="objectives_useloop">Display content of Loop plugin</string>
<string name="objectives_usescale">Use scale function by long-pressing BG chart</string>
<string name="objectives_button_enter">Enter</string>
<string name="enter_code_obtained_from_developers_to_bypass_the_rest_of_objectives">If you have at least 3 months of closed loop experience with other systems you might qualify for a code to skip objectives. See https://androidaps.readthedocs.io/en/latest/EN/Usage/Objectives.html#skip-objectives for details.</string>
<string name="codeaccepted">Code accepted</string>
<string name="codeinvalid">Code invalid</string>
<string name="objectives_exam_objective">Prove your knowledge</string>
<string name="objectives_exam_gate">Study the questions.  You are given four possible answers for each question. There may be more than one correct answer. Please check all those that are correct and select VERIFY.</string>
<string name="objectives_exam_learned_prerequisites">You don\'t need to be a developer but you need NS to pass Objectives, compatible pump and computer to build AAPS for the first time and for every update.</string>
<string name="objectives_exam_learned_prerequisites2">AAPS can be run offline.</string>
<string name="objectives_exam_learned_basaltest">Profile should be reviewed and updated. Better profile = better results.</string>
<string name="objectives_exam_learned_dia">DIA in looping has different meaning (time until all insulin is absorbed) then in classic pump therapy (time until most insulin is absorbed).</string>
<string name="objectives_exam_learned_isf">You learned meaning of ISF value and it affects amount of insulin used for BG correction.</string>
<string name="objectives_exam_learned_ic">You learned meaning of IC value and it affects amount of insulin needed to cover carbs.</string>
<string name="objectives_exam_learned_hypott">Hypo temp target is used only to prevent overcorrection after hypo, when there is usually stacked negative IOB. Additional actions should be evaluated to prevent this situation again in the future.</string>
<string name="objectives_exam_learned_profileswitch">Using percentage doesn\'t affect targets BGs but basal, ISF and IC are adjusted to give more insulin (above 100%) or less insulin (under 100%).</string>
<string name="objectives_exam_learned_profileswitchtime">By using timeshift in profile switch you can adapt circadian profile to irregularities like late wake up.</string>
<string name="objectives_exam_learned_profileswitch4">Editing profile itself is not sufficient to make a change. You still need to do a profile switch to activate changes.</string>
<string name="objectives_exam_learned_exercise">You should reduce amount of insulin in the body at least 1 hour before starting exercise by selecting profile under 100%.</string>
<string name="objectives_exam_learned_exercise2">Setting of higher temp target at least 1 hour before exercise helps with reducing of amount of insulin in the body as well.</string>
<string name="objectives_exam_learned_noisycgm">Wen you cannot trust CGM values you should not use loop unattended.</string>
<string name="objectives_exam_learned_pumpdisconnect">AAPS must know that pump is not connected to count IOB correctly.</string>
<string name="objectives_exam_learned_insulin">Letting know which insulin is in pump is necessary because it does affect IOB calculation.</string>
<string name="objectives_exam_learned_sensitivity">Sensitivity detection is part of the algorithm which allows adapt insulin dosage to different situations.</string>
<string name="objectives_exam_learned_objectives">Your progress in Objectives is stored along with other settings and it should be backed up.</string>
<string name="objectives_exam_learned_objectives2">Always keep exported settings and generated APK also outside phone. Phone can be lost, damaged etc. Good place can be any cloud accessible from phone. In this case you can restore AAPS on another device in a few minutes. Good practice is store used master password on a safe place too. Without it is your backup useless. The same is valid for signing keys (.jks file) and passwords used to build AAPS. The rest is not important - it can be anytime downloaded from internet again.</string>
<string name="objectives_exam_learned_wrongcarbs">If you made wrong input to AAPS or it doesn\'t match reality, resolve it asap. It could lead to overdosing. AAPS should be always informed the best possible way about carbs, insulin etc.</string>
<string name="objectives_exam_learned_iob">If you analyze if AAPS is working well for you watching IOB graph can give you a lof of useful information.</string>
<string name="objectives_exam_learned_cob">COB calculation depends on relation between ISF an IC. Increasing ISF or decreasing IC will lead to longer absorption times. But profile switch with percentage change both values at the same time and absorption time is not affected.</string>
<string name="objectives_exam_learned_breadgrams">Only grams are allowed for entry of carbohydrates.</string>
<string name="objectives_exam_learned_ecarbs">E-carbs is a replacement for extended bolus in pump world.</string>
<string name="objectives_exam_learned_nsclient">Remote monitoring and control is possible but not all features must be available remotely.</string>
<string name="answerdisabledto">Answering disabled until: %1$s</string>
<string name="wronganswer">Wrong answer!</string>
<string name="unfinshed_button">Next unfinished</string>
@ -54,5 +83,6 @@
<string name="notconnected">Not connected to the internet</string>
<string name="failedretrievetime">Failed retrieve time</string>
<string name="requirementnotmet">Objective requirements not met</string>
<string name="what_i_ve_learned">What I\'ve learned:</string>
</resources>

View file

@ -1092,15 +1092,16 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
private fun updateSensitivity() {
_binding ?: return
if (constraintChecker.isAutosensModeEnabled().value() || !(config.NSCLIENT && overviewData.lastAutosensData(iobCobCalculator) == null)) {
val lastAutosensData = overviewData.lastAutosensData(iobCobCalculator)
if (constraintChecker.isAutosensModeEnabled().value() || !(config.NSCLIENT && lastAutosensData == null)) {
binding.infoLayout.sensitivityIcon.setImageResource(info.nightscout.core.main.R.drawable.ic_swap_vert_black_48dp_green)
} else {
binding.infoLayout.sensitivityIcon.setImageResource(info.nightscout.core.main.R.drawable.ic_x_swap_vert)
}
binding.infoLayout.sensitivity.text =
overviewData.lastAutosensData(iobCobCalculator)?.let { autosensData ->
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
lastAutosensData?.let {
String.format(Locale.ENGLISH, "%.0f%%", it.autosensResult.ratio * 100)
} ?: ""
// Show variable sensitivity
val profile = profileFunction.getProfile()

View file

@ -29,6 +29,7 @@ import info.nightscout.database.impl.transactions.CancelCurrentOfflineEventIfAny
import info.nightscout.database.impl.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.interfaces.ApsMode
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit
@ -125,7 +126,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val commands = mapOf(
"BG" to "BG",
"LOOP" to "LOOP STOP/DISABLE/START/ENABLE/RESUME/STATUS\nLOOP SUSPEND 20",
"LOOP" to "LOOP STOP/DISABLE/START/ENABLE/RESUME/STATUS/CLOSED/LGS\nLOOP SUSPEND 20",
"NSCLIENT" to "NSCLIENT RESTART",
"PUMP" to "PUMP\nPUMP CONNECT\nPUMP DISCONNECT 30\n",
"BASAL" to "BASAL STOP/CANCEL\nBASAL 0.3\nBASAL 0.3 20\nBASAL 30%\nBASAL 30% 20\n",
@ -416,10 +417,10 @@ class SmsCommunicatorPlugin @Inject constructor(
receivedSms.processed = true
}
"STATUS" -> {
"STATUS" -> {
val reply = if (loop.enabled) {
if (loop.isSuspended) rh.gs(R.string.sms_loop_suspended_for, loop.minutesToEndOfSuspend())
else rh.gs(R.string.smscommunicator_loop_is_enabled)
else rh.gs(R.string.smscommunicator_loop_is_enabled) + " - " + getApsModeText()
} else
rh.gs(info.nightscout.core.ui.R.string.loopisdisabled)
sendSMS(Sms(receivedSms.phoneNumber, reply))
@ -454,7 +455,7 @@ class SmsCommunicatorPlugin @Inject constructor(
})
}
"SUSPEND" -> {
"SUSPEND" -> {
var duration = 0
if (divided.size == 3) duration = SafeParse.stringToInt(divided[2])
duration = max(0, duration)
@ -502,7 +503,37 @@ class SmsCommunicatorPlugin @Inject constructor(
}
}
else -> sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrong_format)))
"LGS" -> {
val passCode = generatePassCode()
val reply = rh.gs(R.string.smscommunicator_set_lgs_reply_with_code, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() {
uel.log(Action.LGS_LOOP_MODE, Sources.SMS)
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.LGS.name)
rxBus.send(EventPreferenceChange(rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)))
val replyText = rh.gs(R.string.smscommunicator_current_loop_mode, getApsModeText())
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
}
})
}
"CLOSED" -> {
val passCode = generatePassCode()
val reply = rh.gs(R.string.smscommunicator_set_closed_loop_reply_with_code, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() {
uel.log(Action.CLOSED_LOOP_MODE, Sources.SMS)
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.CLOSED.name)
rxBus.send(EventPreferenceChange(rh.gs(info.nightscout.core.ui.R.string.closedloop)))
val replyText = rh.gs(R.string.smscommunicator_current_loop_mode, getApsModeText())
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
}
})
}
else -> sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrong_format)))
}
}
@ -1251,4 +1282,12 @@ class SmsCommunicatorPlugin @Inject constructor(
knownNumbers.size > 1
} ?: false
}
}
private fun getApsModeText(): String =
when (ApsMode.fromString(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name))) {
ApsMode.OPEN -> rh.gs(info.nightscout.core.ui.R.string.openloop)
ApsMode.CLOSED -> rh.gs(info.nightscout.core.ui.R.string.closedloop)
ApsMode.LGS -> rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)
else -> rh.gs(info.nightscout.core.ui.R.string.unknown)
}
}

View file

@ -112,7 +112,8 @@ class StatusLightHandler @Inject constructor(
private fun handleLevel(view: TextView?, criticalSetting: Int, criticalDefaultValue: Double, warnSetting: Int, warnDefaultValue: Double, level: Double, units: String) {
val resUrgent = sp.getDouble(criticalSetting, criticalDefaultValue)
val resWarn = sp.getDouble(warnSetting, warnDefaultValue)
view?.text = " " + DecimalFormatter.to0Decimal(level, units)
if (level > 0) view?.text = " " + DecimalFormatter.to0Decimal(level, units)
else view?.text = ""
warnColors.setColorInverse(view, level, resWarn, resUrgent)
}

View file

@ -135,7 +135,7 @@
android:layout_height="wrap_content"
android:paddingStart="4sp"
android:paddingEnd="4sp"
android:text="Pump: running"
tools:text="Pump: running"
android:textSize="16sp"
tools:ignore="HardcodedText" />
@ -146,7 +146,7 @@
android:layout_weight="1"
android:paddingStart="4sp"
android:paddingEnd="4sp"
android:text="OpenAPS: 3 min ago"
tools:text="OpenAPS: 3 min ago"
android:textSize="16sp"
tools:ignore="HardcodedText" />
@ -157,7 +157,7 @@
android:layout_weight="1"
android:paddingStart="4sp"
android:paddingEnd="4sp"
android:text="Uploader: 84%"
tools:text="Uploader: 84%"
android:textSize="16sp"
tools:ignore="HardcodedText" />

View file

@ -172,8 +172,8 @@
<string name="pb_label">Batería de la bomba</string>
<string name="cannula">Cánula</string>
<string name="ebstopsloop">El uso de la función de bolo extendido detendrá el modo de bucle cerrado durante el tiempo de ejecución del bolo extendido. ¿Realmente quieres esto?</string>
<string name="statuslights_cannula_age">edad de la cánula</string>
<string name="statuslights_patch_pump_age">edad del parche de la bomba</string>
<string name="statuslights_cannula_age">Edad de la cánula</string>
<string name="statuslights_patch_pump_age">Edad de la bomba parche</string>
<string name="patch_pump">Bomba parche</string>
<!-- Overview -->
<string name="show_statuslights">Mostrar luces de estado en la pantalla de inicio</string>
@ -309,7 +309,7 @@
<string name="largedisplay_description">Pantalla grande</string>
<string name="skin">Tema</string>
<!-- DataBroadcast-->
<string name="cannula_usage">uso:</string>
<string name="cannula_usage">Uso:</string>
<!-- Iob-->
<string name="send_logfiles">Enviar los archivos de registro de hoy a los desarrolladores. Situación inesperada.</string>
<!-- Wear-->

View file

@ -85,6 +85,9 @@
<string name="smscommunicator_message_body">Błędny tekst wiadomości</string>
<string name="smscommunicator_report_pump_unreachable_summary">Wyślij SMS, jeśli wyzwolone jest zdarzenie pompy nieosiągalnej</string>
<string name="smscommunicator_pump_unreachable">Zgłoś nieosiągalną pompę</string>
<string name="smscommunicator_set_lgs_reply_with_code">Aby przełączyć pętlę w tryb LGS (zawieszenie przy niskiej glikemii) wprowadź kod %1$s</string>
<string name="smscommunicator_set_closed_loop_reply_with_code">Aby przełączyć pętlę w tryb pętli zamkniętej wprowadź kod %1$s</string>
<string name="smscommunicator_current_loop_mode">Obecny tryb pętli: %1$s</string>
<string name="wrong_format">Błędny format</string>
<string name="sms_actual_bg">BG:</string>
<string name="sms_last_bg">Ostatnia BG:</string>

View file

@ -95,6 +95,9 @@
<string name="smscommunicator_message_body">Invalid message body</string>
<string name="smscommunicator_report_pump_unreachable_summary">Send SMS if unreachable pump event is triggered</string>
<string name="smscommunicator_pump_unreachable">Report pump unreachable</string>
<string name="smscommunicator_set_lgs_reply_with_code">In order to switch Loop mode to LGS (Low Glucose Suspend) reply with code %1$s</string>
<string name="smscommunicator_set_closed_loop_reply_with_code">In order to switch Loop mode to Closed loop reply with code %1$s</string>
<string name="smscommunicator_current_loop_mode">Current loop mode: %1$s</string>
<string name="wrong_format">Wrong format</string>
<string name="sms_actual_bg">BG:</string>
<string name="sms_last_bg">Last BG:</string>

View file

@ -11,6 +11,7 @@ import info.nightscout.database.impl.transactions.CancelCurrentOfflineEventIfAny
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.database.impl.transactions.Transaction
import info.nightscout.interfaces.ApsMode
import info.nightscout.implementation.iob.GlucoseStatusProviderImpl
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit
@ -85,6 +86,10 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
private lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
private var hasBeenRun = false
private val modeClosed = "Closed Loop"
private val modeOpen = "Open Loop"
private val modeLgs = "Low Glucose Suspend"
private val modeUnknown = "unknown"
@BeforeEach fun prepareTests() {
val reading = GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = 1514766900000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)
@ -248,7 +253,13 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
`when`(rh.gsNotLocalised(R.string.smscommunicator_tempbasal_canceled)).thenReturn("Temp basal canceled")
`when`(rh.gsNotLocalised(R.string.smscommunicator_calibration_sent)).thenReturn("Calibration sent. Receiving must be enabled in xDrip+.")
`when`(rh.gsNotLocalised(R.string.smscommunicator_tt_canceled)).thenReturn("Temp Target canceled successfully")
`when`(rh.gs(info.nightscout.core.ui.R.string.closedloop)).thenReturn(modeClosed)
`when`(rh.gs(info.nightscout.core.ui.R.string.openloop)).thenReturn(modeOpen)
`when`(rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)).thenReturn(modeLgs)
`when`(rh.gs(info.nightscout.core.ui.R.string.unknown)).thenReturn(modeUnknown)
`when`(rh.gs(R.string.smscommunicator_set_closed_loop_reply_with_code)).thenReturn("In order to switch Loop mode to Closed loop reply with code %1\$s")
`when`(rh.gs(R.string.smscommunicator_current_loop_mode)).thenReturn("Current loop mode: %1\$s")
`when`(rh.gs(R.string.smscommunicator_set_lgs_reply_with_code)).thenReturn("In order to switch Loop mode to LGS (Low Glucose Suspend) reply with code %1\$s")
}
@Test
@ -329,15 +340,40 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assertions.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages[0].text)
Assertions.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.messages[1].text)
//LOOP STATUS : enabled
//LOOP STATUS : enabled - APS mode - Closed
`when`(loop.enabled).thenReturn(true)
`when`(loop.isSuspended).thenReturn(false)
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.CLOSED.name)
smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP STATUS")
smsCommunicatorPlugin.processSms(sms)
Assertions.assertFalse(sms.ignored)
Assertions.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages[0].text)
Assertions.assertEquals("Loop is enabled", smsCommunicatorPlugin.messages[1].text)
Assertions.assertEquals("Loop is enabled - $modeClosed", smsCommunicatorPlugin.messages[1].text)
//LOOP STATUS : enabled - APS mode - Open
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.OPEN.name)
smsCommunicatorPlugin.messages = ArrayList()
smsCommunicatorPlugin.processSms(sms)
Assertions.assertFalse(sms.ignored)
Assertions.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages[0].text)
Assertions.assertEquals("Loop is enabled - $modeOpen", smsCommunicatorPlugin.messages[1].text)
//LOOP STATUS : enabled - APS mode - LGS
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.LGS.name)
smsCommunicatorPlugin.messages = ArrayList()
smsCommunicatorPlugin.processSms(sms)
Assertions.assertFalse(sms.ignored)
Assertions.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages[0].text)
Assertions.assertEquals("Loop is enabled - $modeLgs", smsCommunicatorPlugin.messages[1].text)
//LOOP STATUS : enabled - APS mode - unknown
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn("some wrong value")
smsCommunicatorPlugin.messages = ArrayList()
smsCommunicatorPlugin.processSms(sms)
Assertions.assertFalse(sms.ignored)
Assertions.assertEquals("LOOP STATUS", smsCommunicatorPlugin.messages[0].text)
Assertions.assertEquals("Loop is enabled - $modeUnknown", smsCommunicatorPlugin.messages[1].text)
//LOOP : wrong format
`when`(loop.enabled).thenReturn(true)
@ -480,6 +516,37 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assertions.assertEquals("LOOP BLABLA", smsCommunicatorPlugin.messages[0].text)
Assertions.assertEquals("Wrong format", smsCommunicatorPlugin.messages[1].text)
//LOOP CLOSED
var smsCommand = "LOOP CLOSED"
val replyClosed = "In order to switch Loop mode to Closed loop reply with code "
`when`(loop.enabled).thenReturn(true)
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.CLOSED.name)
smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", smsCommand)
smsCommunicatorPlugin.processSms(sms)
Assertions.assertFalse(sms.ignored)
Assertions.assertEquals(smsCommand, smsCommunicatorPlugin.messages[0].text)
Assertions.assertTrue(smsCommunicatorPlugin.messages[1].text.contains(replyClosed))
passCode = smsCommunicatorPlugin.messageToConfirm?.confirmCode!!
smsCommunicatorPlugin.processSms(Sms("1234", passCode))
Assertions.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text)
Assertions.assertEquals("Current loop mode: $modeClosed", smsCommunicatorPlugin.messages[3].text)
//LOOP LGS
smsCommand = "LOOP LGS"
val replyLgs = "In order to switch Loop mode to LGS (Low Glucose Suspend) reply with code "
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)).thenReturn(ApsMode.LGS.name)
smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", smsCommand)
smsCommunicatorPlugin.processSms(sms)
Assertions.assertFalse(sms.ignored)
Assertions.assertEquals(smsCommand, smsCommunicatorPlugin.messages[0].text)
Assertions.assertTrue(smsCommunicatorPlugin.messages[1].text.contains(replyLgs))
passCode = smsCommunicatorPlugin.messageToConfirm?.confirmCode!!
smsCommunicatorPlugin.processSms(Sms("1234", passCode))
Assertions.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text)
Assertions.assertEquals("Current loop mode: $modeLgs", smsCommunicatorPlugin.messages[3].text)
//NSCLIENT RESTART
`when`(loop.isEnabled()).thenReturn(true)
`when`(loop.isSuspended).thenReturn(false)

View file

@ -1,7 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Source -->
<string name="ns_client_bg">NSClient Glucosa</string>
<string name="ns_client_bg_short">NS BG</string>
<string name="description_source_ns_client">Recibir los datos de glucosa de Nightscout</string>
<string name="xdrip">xDrip+</string>
<string name="description_source_xdrip">Recibir los valores de glucosa de xDrip+</string>
<string name="dexcom_app_patched">Dexcom (BYODA)</string>
<string name="dexcom_short">BYODA</string>
<string name="description_source_dexcom">Recibir los valores de glucosa de la aplicación Dexcom \'Build Your Own Device\'</string>
<string name="eversense">Eversense App (parchada)</string>
<string name="description_source_eversense">Recibir los valores de glucosa de la aplicación Eversense parchada.</string>
<string name="glimp">Glimp</string>
<string name="description_source_glimp">Recibir valores de glucosa de Glimp.</string>
<string name="mm640g">MM640g</string>
<string name="description_source_mm640g">Recibir los valores de glucosa del 600SeriesAndroidUploader.</string>
<string name="poctech">Poctech</string>
<string name="description_source_poctech">Recibir los valores de glucosa de Poctech</string>
<string name="glunovo">Glunovo</string>
<string name="description_source_glunovo">Recibir los valores de glucosa de la aplicación Glunovo</string>
<string name="intelligo">Intelligo</string>

View file

@ -35,4 +35,5 @@
<string name="bgsource_upload">Innstillinger for opplasting av BS</string>
<string name="dexcom_log_ns_sensor_change_title">Logg sensor bytte til NS</string>
<string name="dexcom_log_ns_sensor_change_summary">Opprett hendelse \"Sensor bytte\" automatisk i NS ved start av sensoren</string>
<string name="direction">retning</string>
</resources>

View file

@ -7,9 +7,9 @@ import info.nightscout.interfaces.nsclient.NSSettingsStatus
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.plugins.sync.nsShared.DataSyncSelectorImplementation
import info.nightscout.plugins.sync.nsShared.NSClientFragment
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclient.DataSyncSelectorImplementation
import info.nightscout.plugins.sync.nsclient.data.NSSettingsStatusImpl
import info.nightscout.plugins.sync.nsclient.data.ProcessedDeviceStatusDataImpl
import info.nightscout.plugins.sync.nsclient.services.NSClientService
@ -17,6 +17,7 @@ import info.nightscout.plugins.sync.nsclient.workers.NSClientAddAckWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientAddUpdateWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientMbgWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientUpdateRemoveAckWorker
import info.nightscout.plugins.sync.nsclientV3.workers.DataSyncWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadDeviceStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker
@ -49,6 +50,7 @@ abstract class SyncModule {
@ContributesAndroidInjector abstract fun contributesTreatmentWorker(): LoadTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesProcessTreatmentsWorker(): ProcessTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesLoadDeviceStatusWorker(): LoadDeviceStatusWorker
@ContributesAndroidInjector abstract fun contributesDataSyncWorker(): DataSyncWorker
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment

View file

@ -1,4 +1,4 @@
package info.nightscout.plugins.sync.nsclient
package info.nightscout.plugins.sync.nsShared
import info.nightscout.database.ValueWrapper
import info.nightscout.database.impl.AppRepository
@ -56,11 +56,12 @@ class DataSyncSelectorImplementation @Inject constructor(
}
private val queueCounter = QueueCounter()
private val isPaused get() = sp.getBoolean(R.string.key_ns_client_paused, false)
override fun queueSize(): Long = queueCounter.size()
override fun doUpload() {
if (sp.getBoolean(R.string.key_ns_upload, true)) {
if (sp.getBoolean(R.string.key_ns_upload, true) && !isPaused) {
processChangedBolusesCompat()
processChangedCarbsCompat()
processChangedBolusCalculatorResultsCompat()
@ -106,6 +107,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedBolusesCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
@ -158,6 +160,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedCarbsCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
@ -206,6 +209,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedBolusCalculatorResultsCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
@ -257,6 +261,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedTempTargetsCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
@ -309,6 +314,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedFoodsCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0)
@ -357,6 +363,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedGlucoseValuesCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
@ -369,7 +376,7 @@ class DataSyncSelectorImplementation @Inject constructor(
aapsLogger.info(LTag.NSCLIENT, "Loading GlucoseValue data Start: $startId ${gv.first} forID: ${gv.second.id} ")
if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) {
when {
// new record with existing NS id => must be coming from NS => ignore
// new record with existing NS id => must be coming from NS => ignore
gv.first.id == gv.second.id && gv.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Loaded from NS: ${gv.second.id} ")
confirmLastGlucoseValueIdIfGreater(gv.second.id)
@ -410,6 +417,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedTherapyEventsCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
@ -458,6 +466,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override fun processChangedDeviceStatusesCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
@ -471,7 +480,7 @@ class DataSyncSelectorImplementation @Inject constructor(
when {
// without nsId = create new
deviceStatus.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.dbAdd("devicestatus", DataSyncSelector.PairDeviceStatus(deviceStatus, null), "$startId/$lastDbId")
activePlugin.activeNsClient?.dbAdd("devicestatus", DataSyncSelector.PairDeviceStatus(deviceStatus, 0), "$startId/$lastDbId")
// with nsId = ignore
deviceStatus.interfaceIDs.nightscoutId != null -> Any()
}
@ -487,6 +496,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedTemporaryBasalsCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
@ -539,6 +549,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedExtendedBolusesCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
@ -599,6 +610,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedProfileSwitchesCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
@ -647,6 +659,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedEffectiveProfileSwitchesCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastEffectiveProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
@ -699,6 +712,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override tailrec fun processChangedOfflineEventsCompat() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
@ -744,6 +758,7 @@ class DataSyncSelectorImplementation @Inject constructor(
}
override fun processChangedProfileStore() {
if (isPaused) return
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)
if (lastChange == 0L) return
@ -753,4 +768,4 @@ class DataSyncSelectorImplementation @Inject constructor(
activePlugin.activeNsClient?.dbAdd("profile", DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "")
}
}
}
}

View file

@ -26,7 +26,6 @@ import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.databinding.NsClientFragmentBinding
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientRestart
@ -56,7 +55,6 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
const val ID_MENU_RESTART = 508
const val ID_MENU_SEND_NOW = 509
const val ID_MENU_FULL_SYNC = 510
const val ID_MENU_TEST = 601
}
override var plugin: PluginBase? = null
@ -97,8 +95,6 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
}
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
if (config.isUnfinishedMode())
menu.add(Menu.FIRST, ID_MENU_TEST, 0, "Test").setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_CLEAR_LOG, 0, rh.gs(R.string.clear_log)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_RESTART, 0, rh.gs(R.string.restart)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(Menu.FIRST, ID_MENU_SEND_NOW, 0, rh.gs(R.string.deliver_now)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
@ -133,11 +129,6 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
true
}
ID_MENU_TEST -> {
nsClientPlugin?.let { plugin -> if (plugin is NSClientV3Plugin) handler.post { plugin.test() } }
true
}
else -> false
}

View file

@ -58,16 +58,6 @@ import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.localmodel.treatment.NSBolus
import info.nightscout.sdk.localmodel.treatment.NSBolusWizard
import info.nightscout.sdk.localmodel.treatment.NSCarbs
import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch
import info.nightscout.sdk.localmodel.treatment.NSExtendedBolus
import info.nightscout.sdk.localmodel.treatment.NSOfflineEvent
import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch
import info.nightscout.sdk.localmodel.treatment.NSTemporaryBasal
import info.nightscout.sdk.localmodel.treatment.NSTemporaryTarget
import info.nightscout.sdk.localmodel.treatment.NSTherapyEvent
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import java.util.concurrent.Executors
@ -203,7 +193,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it")
inserted.inc(NSBolus::class.java.simpleName)
inserted.inc(Bolus::class.java.simpleName)
}
result.invalidated.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -216,19 +206,19 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it")
invalidated.inc(NSBolus::class.java.simpleName)
invalidated.inc(Bolus::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of bolus $it")
nsIdUpdated.inc(NSBolus::class.java.simpleName)
nsIdUpdated.inc(Bolus::class.java.simpleName)
}
result.updated.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated amount of bolus $it")
updated.inc(NSBolus::class.java.simpleName)
updated.inc(Bolus::class.java.simpleName)
}
}
sendLog("Bolus", NSBolus::class.java.simpleName)
sendLog("Bolus", Bolus::class.java.simpleName)
SystemClock.sleep(pause)
if (carbs.isNotEmpty())
@ -250,7 +240,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it")
inserted.inc(NSCarbs::class.java.simpleName)
inserted.inc(Carbs::class.java.simpleName)
}
result.invalidated.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -263,7 +253,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it")
invalidated.inc(NSCarbs::class.java.simpleName)
invalidated.inc(Carbs::class.java.simpleName)
}
result.updated.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -276,16 +266,16 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Updated carbs $it")
updated.inc(NSCarbs::class.java.simpleName)
updated.inc(Carbs::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId carbs $it")
nsIdUpdated.inc(NSCarbs::class.java.simpleName)
nsIdUpdated.inc(Carbs::class.java.simpleName)
}
}
sendLog("Carbs", NSCarbs::class.java.simpleName)
sendLog("Carbs", Carbs::class.java.simpleName)
SystemClock.sleep(pause)
if (temporaryTargets.isNotEmpty())
@ -312,7 +302,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryTarget $tt")
inserted.inc(NSTemporaryTarget::class.java.simpleName)
inserted.inc(TemporaryTarget::class.java.simpleName)
}
result.invalidated.forEach { tt ->
if (config.NSCLIENT.not()) userEntries.add(
@ -330,7 +320,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryTarget $tt")
invalidated.inc(NSTemporaryTarget::class.java.simpleName)
invalidated.inc(TemporaryTarget::class.java.simpleName)
}
result.ended.forEach { tt ->
if (config.NSCLIENT.not()) userEntries.add(
@ -348,19 +338,19 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Updated TemporaryTarget $tt")
ended.inc(NSTemporaryTarget::class.java.simpleName)
ended.inc(TemporaryTarget::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryTarget $it")
nsIdUpdated.inc(NSTemporaryTarget::class.java.simpleName)
nsIdUpdated.inc(TemporaryTarget::class.java.simpleName)
}
result.updatedDuration.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated duration TemporaryTarget $it")
durationUpdated.inc(NSTemporaryTarget::class.java.simpleName)
durationUpdated.inc(TemporaryTarget::class.java.simpleName)
}
}
sendLog("TemporaryTarget", NSTemporaryTarget::class.java.simpleName)
sendLog("TemporaryTarget", TemporaryTarget::class.java.simpleName)
SystemClock.sleep(pause)
if (temporaryBasals.isNotEmpty())
@ -386,7 +376,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryBasal $it")
inserted.inc(NSTemporaryBasal::class.java.simpleName)
inserted.inc(TemporaryBasal::class.java.simpleName)
}
result.invalidated.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -403,7 +393,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it")
invalidated.inc(NSTemporaryBasal::class.java.simpleName)
invalidated.inc(TemporaryBasal::class.java.simpleName)
}
result.ended.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -420,19 +410,19 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Ended TemporaryBasal $it")
ended.inc(NSTemporaryBasal::class.java.simpleName)
ended.inc(TemporaryBasal::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryBasal $it")
nsIdUpdated.inc(NSTemporaryBasal::class.java.simpleName)
nsIdUpdated.inc(TemporaryBasal::class.java.simpleName)
}
result.updatedDuration.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated duration TemporaryBasal $it")
durationUpdated.inc(NSTemporaryBasal::class.java.simpleName)
durationUpdated.inc(TemporaryBasal::class.java.simpleName)
}
}
sendLog("TemporaryBasal", NSTemporaryBasal::class.java.simpleName)
sendLog("TemporaryBasal", TemporaryBasal::class.java.simpleName)
SystemClock.sleep(pause)
if (effectiveProfileSwitches.isNotEmpty())
@ -454,7 +444,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted EffectiveProfileSwitch $it")
inserted.inc(NSEffectiveProfileSwitch::class.java.simpleName)
inserted.inc(EffectiveProfileSwitch::class.java.simpleName)
}
result.invalidated.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -467,15 +457,15 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated EffectiveProfileSwitch $it")
invalidated.inc(NSEffectiveProfileSwitch::class.java.simpleName)
invalidated.inc(EffectiveProfileSwitch::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId EffectiveProfileSwitch $it")
nsIdUpdated.inc(NSEffectiveProfileSwitch::class.java.simpleName)
nsIdUpdated.inc(EffectiveProfileSwitch::class.java.simpleName)
}
}
sendLog("EffectiveProfileSwitch", NSEffectiveProfileSwitch::class.java.simpleName)
sendLog("EffectiveProfileSwitch", EffectiveProfileSwitch::class.java.simpleName)
SystemClock.sleep(pause)
if (profileSwitches.isNotEmpty())
@ -497,7 +487,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it")
inserted.inc(NSProfileSwitch::class.java.simpleName)
inserted.inc(ProfileSwitch::class.java.simpleName)
}
result.invalidated.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -510,15 +500,15 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it")
invalidated.inc(NSProfileSwitch::class.java.simpleName)
invalidated.inc(ProfileSwitch::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId ProfileSwitch $it")
nsIdUpdated.inc(NSProfileSwitch::class.java.simpleName)
nsIdUpdated.inc(ProfileSwitch::class.java.simpleName)
}
}
sendLog("ProfileSwitch", NSProfileSwitch::class.java.simpleName)
sendLog("ProfileSwitch", ProfileSwitch::class.java.simpleName)
SystemClock.sleep(pause)
if (bolusCalculatorResults.isNotEmpty())
@ -531,19 +521,19 @@ class StoreDataForDbImpl @Inject constructor(
bolusCalculatorResults.clear()
result.inserted.forEach {
aapsLogger.debug(LTag.DATABASE, "Inserted BolusCalculatorResult $it")
inserted.inc(NSBolusWizard::class.java.simpleName)
inserted.inc(BolusCalculatorResult::class.java.simpleName)
}
result.invalidated.forEach {
aapsLogger.debug(LTag.DATABASE, "Invalidated BolusCalculatorResult $it")
invalidated.inc(NSBolusWizard::class.java.simpleName)
invalidated.inc(BolusCalculatorResult::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId BolusCalculatorResult $it")
nsIdUpdated.inc(NSBolusWizard::class.java.simpleName)
nsIdUpdated.inc(BolusCalculatorResult::class.java.simpleName)
}
}
sendLog("BolusCalculatorResult", NSBolusWizard::class.java.simpleName)
sendLog("BolusCalculatorResult", BolusCalculatorResult::class.java.simpleName)
SystemClock.sleep(pause)
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT)
@ -583,7 +573,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted TherapyEvent $therapyEvent")
inserted.inc(NSTherapyEvent::class.java.simpleName)
inserted.inc(TherapyEvent::class.java.simpleName)
}
result.invalidated.forEach { therapyEvent ->
if (config.NSCLIENT.not()) userEntries.add(
@ -599,19 +589,19 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated TherapyEvent $therapyEvent")
invalidated.inc(NSTherapyEvent::class.java.simpleName)
invalidated.inc(TherapyEvent::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it")
nsIdUpdated.inc(NSTherapyEvent::class.java.simpleName)
nsIdUpdated.inc(TherapyEvent::class.java.simpleName)
}
result.updatedDuration.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it")
durationUpdated.inc(NSTherapyEvent::class.java.simpleName)
durationUpdated.inc(TherapyEvent::class.java.simpleName)
}
}
sendLog("TherapyEvent", NSTherapyEvent::class.java.simpleName)
sendLog("TherapyEvent", TherapyEvent::class.java.simpleName)
SystemClock.sleep(pause)
if (offlineEvents.isNotEmpty())
@ -635,7 +625,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $oe")
inserted.inc(NSOfflineEvent::class.java.simpleName)
inserted.inc(OfflineEvent::class.java.simpleName)
}
result.invalidated.forEach { oe ->
if (config.NSCLIENT.not()) userEntries.add(
@ -651,7 +641,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $oe")
invalidated.inc(NSOfflineEvent::class.java.simpleName)
invalidated.inc(OfflineEvent::class.java.simpleName)
}
result.ended.forEach { oe ->
if (config.NSCLIENT.not()) userEntries.add(
@ -667,19 +657,19 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $oe")
ended.inc(NSOfflineEvent::class.java.simpleName)
ended.inc(OfflineEvent::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId OfflineEvent $it")
nsIdUpdated.inc(NSOfflineEvent::class.java.simpleName)
nsIdUpdated.inc(OfflineEvent::class.java.simpleName)
}
result.updatedDuration.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated duration OfflineEvent $it")
durationUpdated.inc(NSOfflineEvent::class.java.simpleName)
durationUpdated.inc(OfflineEvent::class.java.simpleName)
}
}
sendLog("OfflineEvent", NSOfflineEvent::class.java.simpleName)
sendLog("OfflineEvent", OfflineEvent::class.java.simpleName)
SystemClock.sleep(pause)
if (extendedBoluses.isNotEmpty())
@ -706,7 +696,7 @@ class StoreDataForDbImpl @Inject constructor(
)
if (it.isEmulatingTempBasal) virtualPump.fakeDataDetected = true
aapsLogger.debug(LTag.DATABASE, "Inserted ExtendedBolus $it")
inserted.inc(NSExtendedBolus::class.java.simpleName)
inserted.inc(ExtendedBolus::class.java.simpleName)
}
result.invalidated.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -724,7 +714,7 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Invalidated ExtendedBolus $it")
invalidated.inc(NSExtendedBolus::class.java.simpleName)
invalidated.inc(ExtendedBolus::class.java.simpleName)
}
result.ended.forEach {
if (config.NSCLIENT.not()) userEntries.add(
@ -742,19 +732,19 @@ class StoreDataForDbImpl @Inject constructor(
)
)
aapsLogger.debug(LTag.DATABASE, "Updated ExtendedBolus $it")
ended.inc(NSExtendedBolus::class.java.simpleName)
ended.inc(ExtendedBolus::class.java.simpleName)
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId ExtendedBolus $it")
nsIdUpdated.inc(NSExtendedBolus::class.java.simpleName)
nsIdUpdated.inc(ExtendedBolus::class.java.simpleName)
}
result.updatedDuration.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated duration ExtendedBolus $it")
durationUpdated.inc(NSExtendedBolus::class.java.simpleName)
durationUpdated.inc(ExtendedBolus::class.java.simpleName)
}
}
sendLog("ExtendedBolus", NSExtendedBolus::class.java.simpleName)
sendLog("ExtendedBolus", ExtendedBolus::class.java.simpleName)
SystemClock.sleep(pause)
uel.log(userEntries)
@ -775,7 +765,7 @@ class StoreDataForDbImpl @Inject constructor(
// cancel waiting task to prevent sending multiple posts
scheduledEventPost?.cancel(false)
val task: Runnable = PostRunnable()
scheduledEventPost = eventWorker.schedule(task, 30, TimeUnit.SECONDS)
scheduledEventPost = eventWorker.schedule(task, 10, TimeUnit.SECONDS)
}
private fun updateNsIds() {
@ -787,6 +777,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryTarget $it")
nsIdUpdated.inc(TemporaryTarget::class.java.simpleName)
}
}
@ -798,6 +789,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of GlucoseValue $it")
nsIdUpdated.inc(GlucoseValue::class.java.simpleName)
}
}
@ -809,6 +801,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Food $it")
nsIdUpdated.inc(Food::class.java.simpleName)
}
}
@ -820,6 +813,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TherapyEvent $it")
nsIdUpdated.inc(TherapyEvent::class.java.simpleName)
}
}
@ -831,6 +825,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Bolus $it")
nsIdUpdated.inc(Bolus::class.java.simpleName)
}
}
@ -842,6 +837,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Carbs $it")
nsIdUpdated.inc(Carbs::class.java.simpleName)
}
}
@ -853,6 +849,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of BolusCalculatorResult $it")
nsIdUpdated.inc(BolusCalculatorResult::class.java.simpleName)
}
}
@ -864,6 +861,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryBasal $it")
nsIdUpdated.inc(TemporaryBasal::class.java.simpleName)
}
}
@ -875,6 +873,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of ExtendedBolus $it")
nsIdUpdated.inc(ExtendedBolus::class.java.simpleName)
}
}
@ -886,6 +885,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of ProfileSwitch $it")
nsIdUpdated.inc(ProfileSwitch::class.java.simpleName)
}
}
@ -897,6 +897,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of EffectiveProfileSwitch $it")
nsIdUpdated.inc(EffectiveProfileSwitch::class.java.simpleName)
}
}
@ -908,6 +909,7 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of DeviceStatus $it")
nsIdUpdated.inc(DeviceStatus::class.java.simpleName)
}
}
@ -919,8 +921,21 @@ class StoreDataForDbImpl @Inject constructor(
.also { result ->
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of OfflineEvent $it")
nsIdUpdated.inc(OfflineEvent::class.java.simpleName)
}
}
sendLog("GlucoseValue", GlucoseValue::class.java.simpleName)
sendLog("Bolus", Bolus::class.java.simpleName)
sendLog("Carbs", Carbs::class.java.simpleName)
sendLog("TemporaryTarget", TemporaryTarget::class.java.simpleName)
sendLog("TemporaryBasal", TemporaryBasal::class.java.simpleName)
sendLog("EffectiveProfileSwitch", EffectiveProfileSwitch::class.java.simpleName)
sendLog("ProfileSwitch", ProfileSwitch::class.java.simpleName)
sendLog("BolusCalculatorResult", BolusCalculatorResult::class.java.simpleName)
sendLog("TherapyEvent", TherapyEvent::class.java.simpleName)
sendLog("OfflineEvent", OfflineEvent::class.java.simpleName)
sendLog("ExtendedBolus", ExtendedBolus::class.java.simpleName)
rxBus.send(EventNSClientNewLog("DONE NSIDs", ""))
}
private fun sendLog(item: String, clazz: String) {

View file

@ -36,32 +36,33 @@ class ProcessedDeviceStatusDataImpl @Inject constructor(
// test warning level // color
override fun pumpStatus(nsSettingsStatus: NSSettingsStatus): Spanned {
val pumpData = pumpData ?: return HtmlHelper.fromHtml("")
//String[] ALL_STATUS_FIELDS = {"reservoir", "battery", "clock", "status", "device"};
val string = StringBuilder()
.append("<span style=\"color:${rh.gac(info.nightscout.core.ui.R.attr.nsTitleColor)}\">")
.append(rh.gs(info.nightscout.core.ui.R.string.pump))
.append(": </span>")
//String[] ALL_STATUS_FIELDS = {"reservoir", "battery", "clock", "status", "device"};
val string = StringBuilder()
.append("<span style=\"color:${rh.gac(info.nightscout.core.ui.R.attr.nsTitleColor)}\">")
.append(rh.gs(info.nightscout.core.ui.R.string.pump))
.append(": </span>")
// test warning level
val level = when {
pumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < dateUtil.now() -> ProcessedDeviceStatusData.Levels.URGENT
pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes") -> ProcessedDeviceStatusData.Levels.URGENT
pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("urgentBattP") -> ProcessedDeviceStatusData.Levels.URGENT
!pumpData.isPercent && pumpData.voltage > 0 && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV") -> ProcessedDeviceStatusData.Levels.URGENT
pumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < dateUtil.now() -> ProcessedDeviceStatusData.Levels.WARN
pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes") -> ProcessedDeviceStatusData.Levels.WARN
pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("warnBattP") -> ProcessedDeviceStatusData.Levels.WARN
val pumpData = pumpData ?: return HtmlHelper.fromHtml(string.toString())
// test warning level
val level = when {
pumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < dateUtil.now() -> ProcessedDeviceStatusData.Levels.URGENT
pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes") -> ProcessedDeviceStatusData.Levels.URGENT
pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("urgentBattP") -> ProcessedDeviceStatusData.Levels.URGENT
!pumpData.isPercent && pumpData.voltage > 0 && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV") -> ProcessedDeviceStatusData.Levels.URGENT
pumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < dateUtil.now() -> ProcessedDeviceStatusData.Levels.WARN
pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes") -> ProcessedDeviceStatusData.Levels.WARN
pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("warnBattP") -> ProcessedDeviceStatusData.Levels.WARN
!pumpData.isPercent && pumpData.voltage > 0 && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("warnBattV") -> ProcessedDeviceStatusData.Levels.WARN
else -> ProcessedDeviceStatusData.Levels.INFO
}
string.append("<span style=\"color:${level.toColor()}\">")
val insulinUnit = rh.gs(info.nightscout.core.ui.R.string.insulin_unit_shortname)
// val insulinUnit = rh.gs(info.nightscout.core.ui.R.string.insulin_unit_shortname)
val fields = nsSettingsStatus.pumpExtendedSettingsFields()
if (pumpData.reservoirDisplayOverride != "")
string.append(pumpData.reservoirDisplayOverride).append("$insulinUnit ")
else if (fields.contains("reservoir")) string.append(pumpData.reservoir.toInt()).append("$insulinUnit ")
// Removed here. Same value is in StatusLights
// if (pumpData.reservoirDisplayOverride != "") string.append(pumpData.reservoirDisplayOverride).append("$insulinUnit ")
// else if (fields.contains("reservoir")) string.append(pumpData.reservoir.toInt()).append("$insulinUnit ")
if (fields.contains("battery") && pumpData.isPercent) string.append(pumpData.percent).append("% ")
if (fields.contains("battery") && !pumpData.isPercent) string.append(Round.roundTo(pumpData.voltage, 0.001)).append(" ")
if (fields.contains("clock")) string.append(dateUtil.minAgo(rh, pumpData.clock)).append(" ")
@ -139,13 +140,19 @@ class ProcessedDeviceStatusDataImpl @Inject constructor(
string.append(": </span>")
val iterator: Iterator<*> = uploaderMap.entries.iterator()
var minBattery = 100
var found = false
while (iterator.hasNext()) {
val pair = iterator.next() as Map.Entry<*, *>
val uploader = pair.value as ProcessedDeviceStatusData.Uploader
if (minBattery > uploader.battery) minBattery = uploader.battery
if (minBattery > uploader.battery) {
minBattery = uploader.battery
found = true
}
}
if (found) {
string.append(minBattery)
string.append("%")
}
string.append(minBattery)
string.append("%")
return HtmlHelper.fromHtml(string.toString())
}

View file

@ -1,6 +1,7 @@
package info.nightscout.plugins.sync.nsclient.extensions
import info.nightscout.database.entities.Carbs
import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.shared.utils.DateUtil
@ -8,7 +9,7 @@ import org.json.JSONObject
fun Carbs.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject =
JSONObject()
.put("eventType", if (amount < 12) info.nightscout.database.entities.TherapyEvent.Type.CARBS_CORRECTION.text else info.nightscout.database.entities.TherapyEvent.Type.MEAL_BOLUS.text)
.put("eventType", if (amount < 12) TherapyEvent.Type.CARBS_CORRECTION.text else TherapyEvent.Type.MEAL_BOLUS.text)
.put("carbs", amount)
.put("notes", notes)
.put("created_at", dateUtil.toISOString(timestamp))

View file

@ -580,7 +580,7 @@ class NSClientService : DaggerService() {
socket?.emit("dbUpdate", message, NSUpdateAck("dbUpdate", _id, aapsLogger, rxBus, this, dateUtil, dataWorkerStorage, originalObject))
rxBus.send(
EventNSClientNewLog(
"DBUPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " +
"UPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " +
"" + _id + " " + data + progress
)
)
@ -596,7 +596,7 @@ class NSClientService : DaggerService() {
message.put("collection", collection)
message.put("data", data)
socket?.emit("dbAdd", message, NSAddAck(aapsLogger, rxBus, this, dateUtil, dataWorkerStorage, originalObject))
rxBus.send(EventNSClientNewLog("DBADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data + " " + progress))
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data + " " + progress))
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}

View file

@ -55,7 +55,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdTemporaryTargets.add(pair.value)
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -66,7 +66,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdGlucoseValues.add(pair.value)
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -77,7 +77,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdFoods.add(pair.value)
dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastFoodIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -88,7 +88,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdTherapyEvents.add(pair.value)
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -99,7 +99,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdBoluses.add(pair.value)
dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastBolusIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -110,7 +110,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdCarbs.add(pair.value)
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -121,7 +121,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdBolusCalculatorResults.add(pair.value)
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -132,7 +132,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdTemporaryBasals.add(pair.value)
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -143,7 +143,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdExtendedBoluses.add(pair.value)
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -154,7 +154,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdProfileSwitches.add(pair.value)
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -165,7 +165,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdEffectiveProfileSwitches.add(pair.value)
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
@ -184,7 +184,7 @@ class NSClientAddAckWorker(
}
is PairProfileStore -> {
dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync)
dataSyncSelector.confirmLastProfileStore(ack.originalObject.id)
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id))
}
@ -192,7 +192,7 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdOfflineEvents.add(pair.value)
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting

View file

@ -46,7 +46,7 @@ class NSClientUpdateRemoveAckWorker(
when (ack.originalObject) {
is PairTemporaryTarget -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryTarget" + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTempTargetsCompat()
@ -55,7 +55,7 @@ class NSClientUpdateRemoveAckWorker(
is PairGlucoseValue -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked GlucoseValue " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedGlucoseValuesCompat()
@ -64,7 +64,7 @@ class NSClientUpdateRemoveAckWorker(
is PairFood -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastFoodIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Food " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedFoodsCompat()
@ -73,7 +73,7 @@ class NSClientUpdateRemoveAckWorker(
is PairTherapyEvent -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TherapyEvent " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTherapyEventsCompat()
@ -82,7 +82,7 @@ class NSClientUpdateRemoveAckWorker(
is PairBolus -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastBolusIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Bolus " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedBolusesCompat()
@ -91,7 +91,7 @@ class NSClientUpdateRemoveAckWorker(
is PairCarbs -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Carbs " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedCarbsCompat()
@ -100,7 +100,7 @@ class NSClientUpdateRemoveAckWorker(
is PairBolusCalculatorResult -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked BolusCalculatorResult " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResultsCompat()
@ -109,7 +109,7 @@ class NSClientUpdateRemoveAckWorker(
is PairTemporaryBasal -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryBasal " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasalsCompat()
@ -118,7 +118,7 @@ class NSClientUpdateRemoveAckWorker(
is PairExtendedBolus -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ExtendedBolus " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedExtendedBolusesCompat()
@ -127,7 +127,7 @@ class NSClientUpdateRemoveAckWorker(
is PairProfileSwitch -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedProfileSwitchesCompat()
@ -136,7 +136,7 @@ class NSClientUpdateRemoveAckWorker(
is PairEffectiveProfileSwitch -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedEffectiveProfileSwitchesCompat()
@ -145,7 +145,7 @@ class NSClientUpdateRemoveAckWorker(
is PairOfflineEvent -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id))
// Send new if waiting
dataSyncSelector.processChangedOfflineEventsCompat()

View file

@ -11,11 +11,15 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import dagger.android.HasAndroidInjector
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.database.entities.interfaces.TraceableDBEntry
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType
@ -30,14 +34,20 @@ import info.nightscout.plugins.sync.nsShared.events.EventNSClientResend
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsclient.NsClientReceiverDelegate
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolus
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSCarbs
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSEffectiveProfileSwitch
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSProfileSwitch
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.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.EventNewBG
import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventSWSyncStatus
import info.nightscout.rx.logging.AAPSLogger
@ -54,9 +64,11 @@ import io.reactivex.rxjava3.kotlin.plusAssign
import kotlinx.coroutines.runBlocking
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 javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.max
@Singleton
class NSClientV3Plugin @Inject constructor(
@ -71,7 +83,9 @@ class NSClientV3Plugin @Inject constructor(
private val nsClientReceiverDelegate: NsClientReceiverDelegate,
private val config: Config,
private val dateUtil: DateUtil,
private val uiInteraction: UiInteraction
private val uiInteraction: UiInteraction,
private val storeDataForDb: StoreDataForDb,
private val dataSyncSelector: DataSyncSelector
) : NsClient, Sync, PluginBase(
PluginDescription()
.mainType(PluginType.SYNC)
@ -99,48 +113,33 @@ class NSClientV3Plugin @Inject constructor(
when {
sp.getBoolean(R.string.key_ns_client_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused)
isAllowed.not() -> blockingReason
nsAndroidClient.lastStatus == null -> rh.gs(R.string.not_connected)
nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected)
workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working)
nsAndroidClient.lastStatus?.apiPermissions?.isFull() == true -> rh.gs(info.nightscout.shared.R.string.connected)
nsAndroidClient.lastStatus?.apiPermissions?.isRead() == true -> rh.gs(R.string.read_only)
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)
}
internal lateinit var nsAndroidClient: NSAndroidClient
// private lateinit var nsAndroidRxClient: NSAndroidRxClient
internal var nsAndroidClient: NSAndroidClient? = null
val isAllowed get() = nsClientReceiverDelegate.allowed
val blockingReason get() = nsClientReceiverDelegate.blockingReason
private val isAllowed get() = nsClientReceiverDelegate.allowed
private val blockingReason get() = nsClientReceiverDelegate.blockingReason
private val maxAge = T.days(77).msecs()
internal var lastModified: LastModified? = null // timestamp of last modification for every collection
internal var lastFetched =
LastModified(
LastModified.Collections(
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge
)
) // timestamp of last fetched data for every collection
val maxAge = T.days(77).msecs()
internal var newestDataOnServer: LastModified? = null // timestamp of last modification for every collection provided by server
internal var lastLoadedSrvModified = LastModified(LastModified.Collections()) // max srvLastModified timestamp of last fetched data for every collection
internal var firstLoadContinueTimestamp = LastModified(LastModified.Collections()) // timestamp of last fetched data for every collection during initial load
override fun onStart() {
// context.bindService(Intent(context, NSClientService::class.java), mConnection, Context.BIND_AUTO_CREATE)
super.onStart()
lastFetched = Json.decodeFromString(
lastLoadedSrvModified = Json.decodeFromString(
sp.getString(
R.string.key_ns_client_v3_last_modified,
Json.encodeToString(
LastModified.serializer(),
LastModified(LastModified.Collections(dateUtil.now() - maxAge, dateUtil.now() - maxAge, dateUtil.now() - maxAge, dateUtil.now() - maxAge))
)
Json.encodeToString(LastModified.serializer(), LastModified(LastModified.Collections()))
)
)
lastFetched.collections.entries = max(dateUtil.now() - maxAge, lastFetched.collections.entries)
lastFetched.collections.treatments = max(dateUtil.now() - maxAge, lastFetched.collections.treatments)
lastFetched.collections.profile = max(dateUtil.now() - maxAge, lastFetched.collections.profile)
lastFetched.collections.devicestatus = max(dateUtil.now() - maxAge, lastFetched.collections.devicestatus)
setClient()
@ -160,10 +159,10 @@ class NSClientV3Plugin @Inject constructor(
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()
}, fabricPrivacy::logException)
// disposable += rxBus
// .toObservable(EventAppExit::class.java)
// .observeOn(aapsSchedulers.io)
// .subscribe({ if (nsClientService != null) context.unbindService(mConnection) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ WorkManager.getInstance(context).cancelUniqueWork(JOB_NAME) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNSClientNewLog::class.java)
.observeOn(aapsSchedulers.io)
@ -179,10 +178,18 @@ class NSClientV3Plugin @Inject constructor(
.toObservable(EventNSClientResend::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleExecution() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleExecution() }, fabricPrivacy::logException)
runLoop = Runnable {
executeLoop()
handler.postDelayed(runLoop, REFRESH_INTERVAL)
executeLoop()
}
handler.postDelayed(runLoop, REFRESH_INTERVAL)
executeLoop()
@ -206,9 +213,8 @@ class NSClientV3Plugin @Inject constructor(
preferenceFragment.findPreference<SwitchPreference>(rh.gs(R.string.key_ns_receive_tbr_eb))?.isVisible = config.isEngineeringMode()
}
override val hasWritePermission: Boolean get() = nsAndroidClient.lastStatus?.apiPermissions?.isFull() ?: false
override val connected: Boolean get() = nsAndroidClient.lastStatus != null
override val hasWritePermission: Boolean get() = nsAndroidClient?.lastStatus?.apiPermissions?.isFull() ?: false
override val connected: Boolean get() = nsAndroidClient?.lastStatus != null
override fun clearLog() {
handler.post {
synchronized(listLog) { listLog.clear() }
@ -251,7 +257,7 @@ class NSClientV3Plugin @Inject constructor(
}
override fun resend(reason: String) {
// nsClientService?.resend(reason)
executeLoop()
}
override fun pause(newState: Boolean) {
@ -277,34 +283,45 @@ class NSClientV3Plugin @Inject constructor(
// })
}
override fun updateLatestBgReceivedIfNewer(latestReceived: Long) {
if (latestReceived > lastFetched.collections.entries) {
lastFetched.collections.entries = latestReceived
storeLastFetched()
override fun isFirstLoad(collection: NsClient.Collection) =
when (collection) {
NsClient.Collection.ENTRIES -> lastLoadedSrvModified.collections.entries == 0L
NsClient.Collection.TREATMENTS -> lastLoadedSrvModified.collections.treatments == 0L
}
override fun updateLatestBgReceivedIfNewer(latestReceived: Long) {
if (isFirstLoad(NsClient.Collection.ENTRIES)) firstLoadContinueTimestamp.collections.entries = latestReceived
}
override fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long) {
lastFetched.collections.treatments = latestReceived
storeLastFetched()
if (isFirstLoad(NsClient.Collection.TREATMENTS)) firstLoadContinueTimestamp.collections.treatments = latestReceived
}
override fun resetToFullSync() {
lastFetched = LastModified(
LastModified.Collections(
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge
)
)
firstLoadContinueTimestamp = LastModified(LastModified.Collections())
lastLoadedSrvModified = LastModified(LastModified.Collections())
storeLastFetched()
}
override fun dbAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
dbOperation(collection, dataPair, progress, Operation.CREATE)
}
override fun dbUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
dbOperation(collection, dataPair, progress, Operation.UPDATE)
}
enum class Operation { CREATE, UPDATE }
private val gson: Gson = GsonBuilder().create()
private fun dbOperation(collection: String, 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.toJson(false, dateUtil)
is DataSyncSelector.PairBolus -> dataPair.value.toNSBolus()
is DataSyncSelector.PairCarbs -> dataPair.value.toNSCarbs()
// is DataSyncSelector.PairBolusCalculatorResult -> dataPair.value.toJson(false, dateUtil, profileFunction)
// is DataSyncSelector.PairTemporaryTarget -> dataPair.value.toJson(false, profileFunction.getUnits(), dateUtil)
// is DataSyncSelector.PairFood -> dataPair.value.toJson(false)
@ -312,25 +329,82 @@ class NSClientV3Plugin @Inject constructor(
// is DataSyncSelector.PairTherapyEvent -> dataPair.value.toJson(false, dateUtil)
// is DataSyncSelector.PairTemporaryBasal -> dataPair.value.toJson(false, profileFunction.getProfile(dataPair.value.timestamp), dateUtil)
// is DataSyncSelector.PairExtendedBolus -> dataPair.value.toJson(false, profileFunction.getProfile(dataPair.value.timestamp), dateUtil)
// is DataSyncSelector.PairProfileSwitch -> dataPair.value.toJson(false, dateUtil)
// is DataSyncSelector.PairEffectiveProfileSwitch -> dataPair.value.toJson(false, dateUtil)
is DataSyncSelector.PairProfileSwitch -> dataPair.value.toNSProfileSwitch(dateUtil)
is DataSyncSelector.PairEffectiveProfileSwitch -> dataPair.value.toNSEffectiveProfileSwitch(dateUtil)
// is DataSyncSelector.PairOfflineEvent -> dataPair.value.toJson(false, dateUtil)
else -> null
else -> null
}?.let { data ->
runBlocking {
if (collection == "treatments") {
val result = nsAndroidClient.createTreatment(data)
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 (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)
}
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)
}
// is DataSyncSelector.PairBolusCalculatorResult -> dataPair.value.toJson(false, dateUtil, profileFunction)
// is DataSyncSelector.PairTemporaryTarget -> dataPair.value.toJson(false, profileFunction.getUnits(), dateUtil)
// is DataSyncSelector.PairFood -> dataPair.value.toJson(false)
// is DataSyncSelector.PairGlucoseValue -> dataPair.value.toJson(false, dateUtil)
// is DataSyncSelector.PairTherapyEvent -> dataPair.value.toJson(false, dateUtil)
// is DataSyncSelector.PairTemporaryBasal -> dataPair.value.toJson(false, profileFunction.getProfile(dataPair.value.timestamp), dateUtil)
// is DataSyncSelector.PairExtendedBolus -> dataPair.value.toJson(false, profileFunction.getProfile(dataPair.value.timestamp), dateUtil)
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)
}
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)
}
// is DataSyncSelector.PairOfflineEvent -> dataPair.value.toJson(false, dateUtil)
}
}
} catch (e: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Upload exception", e)
}
}
}
}
}
override fun dbUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
TODO("Not yet implemented")
}
private fun storeLastFetched() {
sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastFetched))
fun storeLastFetched() {
sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified))
}
fun test() {
@ -338,20 +412,19 @@ class NSClientV3Plugin @Inject constructor(
}
fun scheduleNewExecution() {
val toTime = lastFetched.collections.entries + T.mins(6).plus(T.secs(0)).msecs()
if (toTime > dateUtil.now()) {
handler.postDelayed({ executeLoop() }, toTime - dateUtil.now())
rxBus.send(EventNSClientNewLog("NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
}
var toTime = lastLoadedSrvModified.collections.entries + T.mins(6).plus(T.secs(0)).msecs()
if (toTime < dateUtil.now()) toTime = dateUtil.now() + T.mins(1).plus(T.secs(0)).msecs()
handler.postDelayed({ executeLoop() }, toTime - dateUtil.now())
rxBus.send(EventNSClientNewLog("NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
}
private fun executeLoop() {
if (sp.getBoolean(R.string.key_ns_client_paused, false)) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused"))
rxBus.send(EventNSClientNewLog("RUN", "paused"))
return
}
if (!isAllowed) {
rxBus.send(EventNSClientNewLog("NSCLIENT", blockingReason))
rxBus.send(EventNSClientNewLog("RUN", blockingReason))
return
}
if (workIsRunning(arrayOf(JOB_NAME)))
@ -366,8 +439,10 @@ class NSClientV3Plugin @Inject constructor(
)
.then(OneTimeWorkRequest.Builder(LoadLastModificationWorker::class.java).build())
.then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build())
// LoadTreatmentsWorker is enqueued after BG finish
//.then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
// Other Workers are enqueued after BG finish
// LoadTreatmentsWorker
// LoadDeviceStatusWorker
// DataSyncWorker
.enqueue()
}
}
@ -379,4 +454,20 @@ class NSClientV3Plugin @Inject constructor(
return true
return false
}
private val eventWorker = Executors.newSingleThreadScheduledExecutor()
private var scheduledEventPost: ScheduledFuture<*>? = null
private fun scheduleExecution() {
class PostRunnable : Runnable {
override fun run() {
scheduledEventPost = null
executeLoop()
}
}
// cancel waiting task to prevent sending multiple posts
scheduledEventPost?.cancel(false)
val task: Runnable = PostRunnable()
scheduledEventPost = eventWorker.schedule(task, 10, TimeUnit.SECONDS)
}
}

View file

@ -1,7 +1,9 @@
package info.nightscout.plugins.sync.nsclientV3.extensions
import info.nightscout.database.entities.Carbs
import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.sdk.localmodel.treatment.EventType
import info.nightscout.sdk.localmodel.treatment.NSCarbs
fun NSCarbs.toCarbs(): Carbs =
@ -11,6 +13,22 @@ fun NSCarbs.toCarbs(): Carbs =
utcOffset = utcOffset,
amount = carbs,
notes = notes,
duration = duration,
duration = duration ?: 0L,
interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId)
)
fun Carbs.toNSCarbs(): NSCarbs =
NSCarbs(
eventType = EventType.fromString(if (amount < 12) TherapyEvent.Type.CARBS_CORRECTION.text else TherapyEvent.Type.MEAL_BOLUS.text),
isValid = isValid,
date = timestamp,
utcOffset = utcOffset,
carbs = amount,
notes = notes,
duration = if (duration != 0L) duration else null,
identifier = interfaceIDs.nightscoutId,
pumpId = interfaceIDs.pumpId,
pumpType = interfaceIDs.pumpType?.name,
pumpSerial = interfaceIDs.pumpSerial,
endId = interfaceIDs.endId
)

View file

@ -5,6 +5,7 @@ import info.nightscout.core.profile.ProfileSealed
import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.plugins.sync.nsclient.extensions.fromConstant
import info.nightscout.sdk.localmodel.treatment.EventType
import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch
import info.nightscout.shared.utils.DateUtil
@ -30,4 +31,25 @@ fun NSEffectiveProfileSwitch.toEffectiveProfileSwitch(dateUtil: DateUtil): Effec
insulinConfiguration = profileSealed.insulinConfiguration,
interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId)
)
}
}
fun EffectiveProfileSwitch.toNSEffectiveProfileSwitch(dateUtil: DateUtil) : NSEffectiveProfileSwitch =
NSEffectiveProfileSwitch(
eventType = EventType.NOTE,
isValid = isValid,
date = timestamp,
utcOffset = utcOffset,
profileJson = ProfileSealed.EPS(this).toPureNsJson(dateUtil),
originalProfileName = originalProfileName,
originalCustomizedName = originalCustomizedName,
originalTimeshift = originalTimeshift,
originalPercentage = originalPercentage,
originalDuration = originalDuration,
originalEnd = originalEnd,
notes = originalCustomizedName,
identifier = interfaceIDs.nightscoutId,
pumpId = interfaceIDs.pumpId,
pumpType = interfaceIDs.pumpType?.name,
pumpSerial = interfaceIDs.pumpSerial,
endId = interfaceIDs.endId
)

View file

@ -1,11 +1,14 @@
package info.nightscout.plugins.sync.nsclientV3.extensions
import info.nightscout.core.extensions.fromConstant
import info.nightscout.core.extensions.getCustomizedName
import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import info.nightscout.database.entities.ProfileSwitch
import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.sdk.localmodel.treatment.EventType
import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
@ -13,7 +16,7 @@ import info.nightscout.shared.utils.T
fun NSProfileSwitch.toProfileSwitch(activePlugin: ActivePlugin, dateUtil: DateUtil): ProfileSwitch? {
val pureProfile =
profileJson?.let { pureProfileFromJson(it, dateUtil) ?: return null }
?: activePlugin.activeProfileSource.profile?.getSpecificProfile(profileName) ?: return null
?: activePlugin.activeProfileSource.profile?.getSpecificProfile(profile) ?: return null
val profileSealed = ProfileSealed.Pure(pureProfile)
@ -26,11 +29,39 @@ fun NSProfileSwitch.toProfileSwitch(activePlugin: ActivePlugin, dateUtil: DateUt
icBlocks = profileSealed.icBlocks,
targetBlocks = profileSealed.targetBlocks,
glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(profileSealed.units),
profileName = originalProfileName ?: profileName,
profileName = originalProfileName ?: profile,
timeshift = timeShift ?: 0,
percentage = percentage ?: 100,
duration = originalDuration ?: T.mins(duration ?: 0).msecs(),
insulinConfiguration = profileSealed.insulinConfiguration,
interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId)
)
}
fun ProfileSwitch.toNSProfileSwitch(dateUtil: DateUtil): NSProfileSwitch {
val unmodifiedCustomizedName = getCustomizedName()
// ProfileSealed.PS doesn't provide unmodified json -> reset it
val unmodifiedTimeshift = timeshift
val unmodifiedPercentage = percentage
timeshift = 0
percentage = 100
return NSProfileSwitch(
eventType = EventType.fromString(TherapyEvent.Type.PROFILE_SWITCH.text),
isValid = isValid,
date = timestamp,
utcOffset = utcOffset,
timeShift = unmodifiedTimeshift,
percentage = unmodifiedPercentage,
duration = T.mins(duration).msecs(),
profile = unmodifiedCustomizedName,
originalProfileName = profileName,
originalDuration = duration,
profileJson = ProfileSealed.PS(this).toPureNsJson(dateUtil),
identifier = interfaceIDs.nightscoutId,
pumpId = interfaceIDs.pumpId,
pumpType = interfaceIDs.pumpType?.name,
pumpSerial = interfaceIDs.pumpSerial,
endId = interfaceIDs.endId
)
}

View file

@ -0,0 +1,21 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.WorkerParameters
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.sync.DataSyncSelector
import javax.inject.Inject
class DataSyncWorker(
context: Context, params: WorkerParameters
) : LoggingWorker(context, params) {
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var activePlugin: ActivePlugin
override fun doWorkAndLog(): Result {
if (activePlugin.activeNsClient?.hasWritePermission == true) dataSyncSelector.doUpload()
return Result.success()
}
}

View file

@ -8,15 +8,19 @@ import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.workflow.WorkerClasses
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
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
import kotlin.math.max
class LoadBgWorker(
context: Context, params: WorkerParameters
@ -36,21 +40,28 @@ class LoadBgWorker(
}
override fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
var ret = Result.success()
val isFirstLoad = nsClientV3Plugin.isFirstLoad(NsClient.Collection.ENTRIES)
val lastLoaded =
if (isFirstLoad) max(nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries, dateUtil.now() - nsClientV3Plugin.maxAge)
else max(nsClientV3Plugin.lastLoadedSrvModified.collections.entries, dateUtil.now() - nsClientV3Plugin.maxAge)
runBlocking {
if ((nsClientV3Plugin.lastModified?.collections?.entries ?: Long.MAX_VALUE) > nsClientV3Plugin.lastFetched.collections.entries)
if ((nsClientV3Plugin.newestDataOnServer?.collections?.entries ?: Long.MAX_VALUE) > lastLoaded)
try {
//val sgvs = nsClientV3Plugin.nsAndroidClient.getSgvsModifiedSince(nsClientV3Plugin.lastFetched.collections.entries)
val sgvs = nsClientV3Plugin.nsAndroidClient.getSgvsNewerThan(nsClientV3Plugin.lastFetched.collections.entries, 500)
val sgvs: List<NSSgvV3>
val response: NSAndroidClient.ReadResponse<List<NSSgvV3>>?
if (isFirstLoad) sgvs = nsAndroidClient.getSgvsNewerThan(lastLoaded, 500)
else {
response = nsAndroidClient.getSgvsModifiedSince(lastLoaded, 500)
sgvs = response.values
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = response.lastServerModified
nsClientV3Plugin.storeLastFetched()
}
aapsLogger.debug("SGVS: $sgvs")
if (sgvs.isNotEmpty()) {
rxBus.send(
EventNSClientNewLog(
"RCV",
"${sgvs.size} SVGs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.entries)}"
)
)
val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
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
@ -60,7 +71,13 @@ class LoadBgWorker(
OneTimeWorkRequest.Builder(workerClasses.nsClientSourceWorker).setInputData(dataWorkerStorage.storeInputData(sgvs)).build()
).then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build()).enqueue()
} else {
rxBus.send(EventNSClientNewLog("END", "No SGVs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.entries)}"))
// End first load
if (isFirstLoad) {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
nsClientV3Plugin.storeLastFetched()
}
rxBus.send(EventNSClientNewLog("RCV END", "No SGVs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.beginUniqueWork(
NSClientV3Plugin.JOB_NAME,
@ -75,7 +92,13 @@ class LoadBgWorker(
ret = Result.failure(workDataOf("Error" to error.toString()))
}
else {
rxBus.send(EventNSClientNewLog("END", "No new SGVs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.entries)}"))
// End first load
if (isFirstLoad) {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
nsClientV3Plugin.storeLastFetched()
}
rxBus.send(EventNSClientNewLog("RCV END", "No new SGVs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
nsClientV3Plugin.scheduleNewExecution() // Idea is to run after 5 min after last BG
WorkManager.getInstance(context)
.beginUniqueWork(

View file

@ -1,6 +1,9 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
@ -27,19 +30,27 @@ class LoadDeviceStatusWorker(
@Inject lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler
override fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
var ret = Result.success()
runBlocking {
try {
val from = dateUtil.now() - T.mins(7).msecs()
val deviceStatuses = nsClientV3Plugin.nsAndroidClient.getDeviceStatusModifiedSince(from)
val deviceStatuses = nsAndroidClient.getDeviceStatusModifiedSince(from)
aapsLogger.debug("DEVICESTATUSES: $deviceStatuses")
if (deviceStatuses.isNotEmpty()) {
rxBus.send(EventNSClientNewLog("RCV", "${deviceStatuses.size} DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
nsDeviceStatusHandler.handleNewData(deviceStatuses.toTypedArray())
rxBus.send(EventNSClientNewLog("DONE DS", ""))
} else {
rxBus.send(EventNSClientNewLog("END", "No DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
rxBus.send(EventNSClientNewLog("RCV END", "No DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
}
WorkManager.getInstance(context)
.enqueueUniqueWork(
NSClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(DataSyncWorker::class.java).build()
)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
ret = Result.failure(workDataOf("Error" to error.toString()))

View file

@ -15,13 +15,14 @@ class LoadLastModificationWorker(
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
override fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
var ret = Result.success()
runBlocking {
try {
val lm = nsClientV3Plugin.nsAndroidClient.getLastModified()
nsClientV3Plugin.lastModified = lm
aapsLogger.debug("LAST MODIFIED: ${nsClientV3Plugin.lastModified}")
val lm = nsAndroidClient.getLastModified()
nsClientV3Plugin.newestDataOnServer = lm
aapsLogger.debug("LAST MODIFIED: ${nsClientV3Plugin.newestDataOnServer}")
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
ret = Result.failure(workDataOf("Error" to error.toString()))

View file

@ -15,11 +15,12 @@ class LoadStatusWorker(
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
override fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
var ret = Result.success()
runBlocking {
try {
val status = nsClientV3Plugin.nsAndroidClient.getStatus()
val status = nsAndroidClient.getStatus()
aapsLogger.debug("STATUS: $status")
} catch (error: Exception) {
aapsLogger.error("Error: ", error)

View file

@ -9,12 +9,16 @@ import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.treatment.NSTreatment
import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
import kotlin.math.max
class LoadTreatmentsWorker(
context: Context,
@ -29,36 +33,50 @@ class LoadTreatmentsWorker(
@Inject lateinit var storeDataForDb: StoreDataForDb
override fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
var ret = Result.success()
val isFirstLoad = nsClientV3Plugin.isFirstLoad(NsClient.Collection.TREATMENTS)
val lastLoaded =
if (isFirstLoad) max(nsClientV3Plugin.firstLoadContinueTimestamp.collections.treatments, dateUtil.now() - nsClientV3Plugin.maxAge)
else max(nsClientV3Plugin.lastLoadedSrvModified.collections.treatments, dateUtil.now() - nsClientV3Plugin.maxAge)
runBlocking {
if ((nsClientV3Plugin.lastModified?.collections?.treatments ?: Long.MAX_VALUE) > nsClientV3Plugin.lastFetched.collections.treatments)
if ((nsClientV3Plugin.newestDataOnServer?.collections?.treatments ?: Long.MAX_VALUE) > lastLoaded)
try {
val treatments = nsClientV3Plugin.nsAndroidClient.getTreatmentsModifiedSince(nsClientV3Plugin.lastFetched.collections.treatments, 500)
val treatments: List<NSTreatment>
val response: NSAndroidClient.ReadResponse<List<NSTreatment>>?
if (isFirstLoad) {
treatments = nsAndroidClient.getTreatmentsNewerThan(lastLoaded, 500)
response = NSAndroidClient.ReadResponse(0, treatments)
}
else {
response = nsAndroidClient.getTreatmentsModifiedSince(lastLoaded, 500)
treatments = response.values
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = response.lastServerModified
nsClientV3Plugin.storeLastFetched()
}
aapsLogger.debug("TREATMENTS: $treatments")
if (treatments.isNotEmpty()) {
rxBus.send(
EventNSClientNewLog(
"RCV",
"${treatments.size} TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.treatments)}"
)
)
val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
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(treatments))
.setInputData(dataWorkerStorage.storeInputData(response))
.build()
).then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
.enqueue()
} else {
rxBus.send(
EventNSClientNewLog(
"END", "No TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.treatments)}"
)
)
// End first load
if (isFirstLoad) {
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded
nsClientV3Plugin.storeLastFetched()
}
rxBus.send(EventNSClientNewLog("RCV END", "No TRs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb()
WorkManager.getInstance(context)
.enqueueUniqueWork(
@ -72,7 +90,13 @@ class LoadTreatmentsWorker(
ret = Result.failure(workDataOf("Error" to error.toString()))
}
else {
rxBus.send(EventNSClientNewLog("END", "No new TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.treatments)}"))
// End first load
if (isFirstLoad) {
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded
nsClientV3Plugin.storeLastFetched()
}
rxBus.send(EventNSClientNewLog("RCV END", "No new TRs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb()
WorkManager.getInstance(context)
.enqueueUniqueWork(

View file

@ -25,6 +25,7 @@ import info.nightscout.plugins.sync.nsclientV3.extensions.toTemporaryTarget
import info.nightscout.plugins.sync.nsclientV3.extensions.toTherapyEvent
import info.nightscout.rx.bus.RxBus
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
@ -58,19 +59,14 @@ class ProcessTreatmentsWorker(
override fun doWorkAndLog(): Result {
@Suppress("UNCHECKED_CAST")
val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as List<NSTreatment>?
val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as NSAndroidClient.ReadResponse<List<NSTreatment>>?
?: return Result.failure(workDataOf("Error" to "missing input data"))
var latestDateInReceivedData: Long = 0
val ret = Result.success()
var latestDateInReceivedData = 0L
for (treatment in treatments) {
for (treatment in treatments.values) {
aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment")
//Find latest date in treatment
val mills = treatment.date
if (mills != 0L && mills < dateUtil.now())
if (mills > latestDateInReceivedData) latestDateInReceivedData = mills
if (treatment.date > latestDateInReceivedData) latestDateInReceivedData = treatment.date
when (treatment) {
is NSBolus ->

View file

@ -97,4 +97,7 @@
<string name="remove_all">Fjern alt</string>
<string name="reset_start">Tilbakestill til oppstart</string>
<string name="upload_now">Last opp nå</string>
<string name="not_connected">Ikke tilkoblet</string>
<string name="read_only">Skrivebeskyttet</string>
<string name="working">Prosesserer</string>
</resources>

View file

@ -151,7 +151,7 @@
android:title="@string/connection_settings_title">
<SwitchPreference
android:defaultValue="false"
android:defaultValue="true"
android:key="@string/key_ns_cellular"
android:title="@string/ns_cellular" />

View file

@ -27,6 +27,7 @@ import info.nightscout.interfaces.pump.defs.PumpType
import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.queue.CustomCommand
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.Round
import info.nightscout.interfaces.utils.TimeChangeType
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
@ -44,6 +45,7 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.abs
import kotlin.math.min
import kotlin.math.roundToInt
@ -311,10 +313,10 @@ class EopatchPumpPlugin @Inject constructor(
disposable.dispose()
return if (isSuccess && askedInsulin == detailedBolusInfo.insulin)
PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(detailedBolusInfo.insulin)
return if (isSuccess && abs(askedInsulin - detailedBolusInfo.insulin) < pumpDescription.bolusStep)
PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(askedInsulin)
else
PumpEnactResult(injector).success(false)/*.enacted(false)*/.bolusDelivered(detailedBolusInfo.insulin)
PumpEnactResult(injector).success(false)/*.enacted(false)*/.bolusDelivered(Round.roundTo(detailedBolusInfo.insulin, 0.01))
} else {
// no bolus required

View file

@ -191,7 +191,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
if (podStateManager.isSuspended) {
showNotification(
Notification.OMNIPOD_POD_SUSPENDED,
"Insulin delivery suspended",
rh.gs(R.string.insulin_delivery_suspended),
Notification.NORMAL,
info.nightscout.core.ui.R.raw.boluserror
)
@ -200,9 +200,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
if (!podStateManager.sameTimeZone) {
uiInteraction.addNotification(
Notification.OMNIPOD_TIME_OUT_OF_SYNC,
"Timezone on pod is different from the timezone on phone. " +
"Basal rate is incorrect" +
"Switch profile to fix",
rh.gs(R.string.timezone_on_pod_is_different_from_the_timezone),
Notification.NORMAL
)
}
@ -425,22 +423,21 @@ class OmnipodDashPumpPlugin @Inject constructor(
if (deliverySuspended) {
showNotification(
Notification.FAILED_UPDATE_PROFILE,
"Failed to set the new basal profile. Delivery suspended",
rh.gs(R.string.failed_to_set_the_new_basal_profile),
Notification.URGENT,
info.nightscout.core.ui.R.raw.boluserror
)
} else {
showNotification(
Notification.FAILED_UPDATE_PROFILE,
"Setting basal profile might have failed. Delivery might be suspended!" +
" Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed.",
rh.gs(R.string.setting_basal_profile_might_have_failed),
Notification.URGENT,
info.nightscout.core.ui.R.raw.boluserror
)
}
Completable.error(java.lang.IllegalStateException("Command not confirmed"))
} else {
showNotification(Notification.PROFILE_SET_OK, "Profile set OK", Notification.INFO, null)
showNotification(Notification.PROFILE_SET_OK, rh.gs(R.string.profile_set_ok), Notification.INFO, null)
Completable.complete()
}
@ -466,8 +463,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
).doOnComplete {
notifyOnUnconfirmed(
Notification.FAILED_UPDATE_PROFILE,
"Suspend delivery is unconfirmed! " +
"Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed.",
rh.gs(R.string.suspend_delivery_is_unconfirmed),
info.nightscout.core.ui.R.raw.boluserror,
)
}
@ -644,7 +640,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
) info.nightscout.core.ui.R.raw.boluserror
else 0
showErrorDialog("Bolus delivery status uncertain. Refresh pod status to confirm or deny.", sound)
showErrorDialog(rh.gs(R.string.bolus_delivery_status_uncertain), sound)
}
}
}.toSingle {
@ -724,7 +720,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
}
for (tryNumber in 1..BOLUS_RETRIES) {
updateBolusProgressDialog("Checking delivery status", 100)
updateBolusProgressDialog(rh.gs(R.string.checking_delivery_status), 100)
val cmd = if (bolusCanceled)
cancelBolus()
@ -856,8 +852,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
).doOnComplete {
notifyOnUnconfirmed(
Notification.OMNIPOD_TBR_ALERTS,
"Setting temp basal might have basal failed. If a temp basal was previously running, " +
"it has been cancelled. Please manually refresh the Pod status from the Omnipod tab.",
rh.gs(R.string.setting_temp_basal_might_have_basal_failed),
info.nightscout.core.ui.R.raw.boluserror,
)
}.toPumpEnactResultImpl()
@ -917,9 +912,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
).doOnComplete {
notifyOnUnconfirmed(
Notification.OMNIPOD_TBR_ALERTS,
"Cancelling temp basal might have failed." +
"If a temp basal was previously running, it might have been cancelled." +
"Please manually refresh the Pod status from the Omnipod tab.", // TODO: i8n
rh.gs(R.string.cancelling_temp_basal_might_have_failed),
info.nightscout.core.ui.R.raw.boluserror,
)
}
@ -967,7 +960,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
).doOnComplete {
notifyOnUnconfirmed(
Notification.OMNIPOD_TBR_ALERTS,
"Cancel temp basal result is uncertain", // TODO: i8n,
rh.gs(R.string.cancel_temp_basal_result_is_uncertain),
info.nightscout.core.ui.R.raw.boluserror, // TODO: add setting for this
)
}.toPumpEnactResultImpl()
@ -1188,7 +1181,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
).doFinally {
notifyOnUnconfirmed(
Notification.FAILED_UPDATE_PROFILE,
"Unconfirmed resumeDelivery command. Please refresh pod status",
rh.gs(R.string.unconfirmed_resumedelivery_command_please_refresh_pod_status),
info.nightscout.core.ui.R.raw.boluserror
)
}.toPumpEnactResultImpl()

View file

@ -53,5 +53,17 @@
<string name="unconfirmed_command" comment="26 characters max for translation">Unconfirmed command</string>
<string name="requested_by_user" comment="26 characters max for translation">Requested by user</string>
<string name="profile_set_ok">Profile set OK</string>
<string name="suspend_delivery_is_unconfirmed">Suspend delivery is unconfirmed! Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed.</string>
<string name="insulin_delivery_suspended">Insulin delivery suspended</string>
<string name="timezone_on_pod_is_different_from_the_timezone">Timezone on pod is different from the timezone on phone. Basal rate is incorrect. Switch profile to fix</string>
<string name="failed_to_set_the_new_basal_profile">Failed to set the new basal profile. Delivery suspended</string>
<string name="setting_basal_profile_might_have_failed">Setting basal profile might have failed. Delivery might be suspended! Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed.</string>
<string name="bolus_delivery_status_uncertain">Bolus delivery status uncertain. Refresh pod status to confirm or deny.</string>
<string name="checking_delivery_status">Checking delivery status</string>
<string name="setting_temp_basal_might_have_basal_failed">Setting temp basal might have basal failed. If a temp basal was previously running, it has been cancelled. Please manually refresh the Pod status from the Omnipod tab.</string>
<string name="cancel_temp_basal_result_is_uncertain">Cancel temp basal result is uncertain</string>
<string name="unconfirmed_resumedelivery_command_please_refresh_pod_status">Unconfirmed resumeDelivery command. Please refresh pod status</string>
<string name="cancelling_temp_basal_might_have_failed">Cancelling temp basal might have failed. If a temp basal was previously running, it might have been cancelled. Please manually refresh the Pod status from the Omnipod tab.</string>
</resources>

View file

@ -12,6 +12,7 @@ import info.nightscout.core.utils.fabric.InstanceId
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.Profile
@ -62,7 +63,8 @@ open class VirtualPumpPlugin @Inject constructor(
commandQueue: CommandQueue,
private val pumpSync: PumpSync,
private val config: Config,
private val dateUtil: DateUtil
private val dateUtil: DateUtil,
private val processedDeviceStatusData: ProcessedDeviceStatusData
) : PumpPluginBase(
PluginDescription()
.mainType(PluginType.PUMP)
@ -169,7 +171,9 @@ open class VirtualPumpPlugin @Inject constructor(
get() = profileFunction.getProfile()?.getBasal() ?: 0.0
override val reservoirLevel: Double
get() = reservoirInUnits.toDouble()
get() =
if (config.NSCLIENT) processedDeviceStatusData.pumpData?.reservoir ?: -1.0
else reservoirInUnits.toDouble()
override val batteryLevel: Int
get() = batteryPercent

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- VirtualPump -->
<string name="virtual_pump_type">Virtuell pumpe</string>
<string name="virtual_pump_definition">Pumpedefinisjon</string>
<string name="virtual_pump_pump_def">Bolus: Step=%1$s\nForlenget bolus: [Step=%2$s, Varighet=%3$smin-%4$sh]\nBasal: Step=%5$s\nTBR: %6$s (av %7$s), Varighet=%8$smin-%9$sh\n%10$s</string>
<string name="virtual_pump_shortname">VPUMP</string>

View file

@ -5,6 +5,7 @@ import info.nightscout.androidaps.TestBase
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.pump.defs.PumpType
@ -31,12 +32,17 @@ class VirtualPumpPluginUTest : TestBase() {
@Mock lateinit var dateUtil: DateUtil
@Mock lateinit var pumpSync: PumpSync
@Mock lateinit var config: Config
@Mock lateinit var processedDeviceStatusData: ProcessedDeviceStatusData
private lateinit var virtualPumpPlugin: VirtualPumpPlugin
@BeforeEach
fun prepareMocks() {
virtualPumpPlugin = VirtualPumpPlugin({ AndroidInjector { } }, aapsLogger, rxBus, fabricPrivacy, rh, aapsSchedulers, sp, profileFunction, iobCobCalculator, commandQueue, pumpSync, config, dateUtil)
virtualPumpPlugin = VirtualPumpPlugin(
{ AndroidInjector { } },
aapsLogger, rxBus, fabricPrivacy, rh, aapsSchedulers, sp, profileFunction, iobCobCalculator,
commandQueue, pumpSync, config, dateUtil, processedDeviceStatusData
)
}
@Test

View file

@ -72,7 +72,9 @@ class CarbsDialog : DialogFragmentWithDate() {
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
validateInputs()
_binding?.let {
validateInputs()
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

View file

@ -80,7 +80,9 @@ class InsulinDialog : DialogFragmentWithDate() {
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
validateInputs()
_binding?.let {
validateInputs()
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

View file

@ -21,6 +21,7 @@ import info.nightscout.database.impl.AppRepository
import info.nightscout.database.impl.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.interfaces.ConfigBuilder
import info.nightscout.interfaces.ApsMode
import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints
@ -161,7 +162,7 @@ class LoopDialog : DaggerDialogFragment() {
val closedLoopAllowed = constraintChecker.isClosedLoopAllowed(Constraint(true))
val closedLoopAllowed2 = activePlugin.activeObjectives?.isAccomplished(Objectives.MAXIOB_OBJECTIVE) ?: false
val lgsEnabled = constraintChecker.isLgsAllowed(Constraint(true))
val apsMode = sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")
val apsMode = ApsMode.fromString(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name))
val pump = activePlugin.activePump
binding.overviewDisconnect15m.visibility = pumpDescription.tempDurationStep15mAllowed.toVisibility()
@ -210,27 +211,27 @@ class LoopDialog : DaggerDialogFragment() {
else -> {
binding.overviewLoop.visibility = View.VISIBLE
binding.overviewEnable.visibility = View.GONE
when {
apsMode == "closed" -> {
when (apsMode) {
ApsMode.CLOSED -> {
binding.overviewCloseloop.visibility = View.GONE
binding.overviewLgsloop.visibility = View.VISIBLE
binding.overviewOpenloop.visibility = View.VISIBLE
}
apsMode == "lgs" -> {
ApsMode.LGS -> {
binding.overviewCloseloop.visibility = closedLoopAllowed.value().toVisibility() //show Close loop button only if Close loop allowed
binding.overviewLgsloop.visibility = View.GONE
binding.overviewOpenloop.visibility = View.VISIBLE
}
apsMode == "open" -> {
ApsMode.OPEN -> {
binding.overviewCloseloop.visibility =
closedLoopAllowed2.toVisibility() //show CloseLoop button only if Objective 6 is completed (closedLoopAllowed always false in open loop mode)
binding.overviewLgsloop.visibility = lgsEnabled.value().toVisibility()
binding.overviewOpenloop.visibility = View.GONE
}
else -> {
else -> {
binding.overviewCloseloop.visibility = View.GONE
binding.overviewLgsloop.visibility = View.GONE
binding.overviewOpenloop.visibility = View.GONE
@ -282,21 +283,21 @@ class LoopDialog : DaggerDialogFragment() {
when (v.id) {
R.id.overview_closeloop -> {
uel.log(UserEntry.Action.CLOSED_LOOP_MODE, UserEntry.Sources.LoopDialog)
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, "closed")
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.CLOSED.name)
rxBus.send(EventPreferenceChange(rh.gs(info.nightscout.core.ui.R.string.closedloop)))
return true
}
R.id.overview_lgsloop -> {
uel.log(UserEntry.Action.LGS_LOOP_MODE, UserEntry.Sources.LoopDialog)
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, "lgs")
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.LGS.name)
rxBus.send(EventPreferenceChange(rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)))
return true
}
R.id.overview_openloop -> {
uel.log(UserEntry.Action.OPEN_LOOP_MODE, UserEntry.Sources.LoopDialog)
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, "open")
sp.putString(info.nightscout.core.utils.R.string.key_aps_mode, ApsMode.OPEN.name)
rxBus.send(EventPreferenceChange(rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)))
return true
}
@ -449,7 +450,7 @@ class LoopDialog : DaggerDialogFragment() {
override fun onResume() {
super.onResume()
if(!queryingProtection) {
if (!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {

View file

@ -65,9 +65,11 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val isDuration = binding.duration.value > 0
val isLowerPercentage = binding.percentage.value < 100
binding.ttLayout.visibility = (isDuration && isLowerPercentage).toVisibility()
_binding?.let { binding ->
val isDuration = binding.duration.value > 0
val isLowerPercentage = binding.percentage.value < 100
binding.ttLayout.visibility = (isDuration && isLowerPercentage).toVisibility()
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

Some files were not shown because too many files have changed in this diff Show more