Merge branch 'upstream-dev' into fix-history-id
This commit is contained in:
commit
770bb93e6b
66 changed files with 4254 additions and 49 deletions
|
@ -28,6 +28,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
|
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
|
||||||
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
|
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
|
||||||
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotunePlugin
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
|
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
|
||||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
|
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
|
||||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
|
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
|
||||||
|
@ -65,6 +66,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
||||||
@Inject lateinit var config: Config
|
@Inject lateinit var config: Config
|
||||||
|
|
||||||
@Inject lateinit var automationPlugin: AutomationPlugin
|
@Inject lateinit var automationPlugin: AutomationPlugin
|
||||||
|
@Inject lateinit var autotunePlugin: AutotunePlugin
|
||||||
@Inject lateinit var danaRPlugin: DanaRPlugin
|
@Inject lateinit var danaRPlugin: DanaRPlugin
|
||||||
@Inject lateinit var danaRKoreanPlugin: DanaRKoreanPlugin
|
@Inject lateinit var danaRKoreanPlugin: DanaRKoreanPlugin
|
||||||
@Inject lateinit var danaRv2Plugin: DanaRv2Plugin
|
@Inject lateinit var danaRv2Plugin: DanaRv2Plugin
|
||||||
|
@ -187,6 +189,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
||||||
addPreferencesFromResourceIfEnabled(tidepoolPlugin, rootKey)
|
addPreferencesFromResourceIfEnabled(tidepoolPlugin, rootKey)
|
||||||
addPreferencesFromResourceIfEnabled(smsCommunicatorPlugin, rootKey)
|
addPreferencesFromResourceIfEnabled(smsCommunicatorPlugin, rootKey)
|
||||||
addPreferencesFromResourceIfEnabled(automationPlugin, rootKey)
|
addPreferencesFromResourceIfEnabled(automationPlugin, rootKey)
|
||||||
|
addPreferencesFromResourceIfEnabled(autotunePlugin, rootKey)
|
||||||
addPreferencesFromResourceIfEnabled(wearPlugin, rootKey)
|
addPreferencesFromResourceIfEnabled(wearPlugin, rootKey)
|
||||||
addPreferencesFromResourceIfEnabled(statusLinePlugin, rootKey)
|
addPreferencesFromResourceIfEnabled(statusLinePlugin, rootKey)
|
||||||
addPreferencesFromResource(R.xml.pref_alerts, rootKey)
|
addPreferencesFromResource(R.xml.pref_alerts, rootKey)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import info.nightscout.androidaps.dana.di.DanaModule
|
||||||
import info.nightscout.androidaps.danar.di.DanaRModule
|
import info.nightscout.androidaps.danar.di.DanaRModule
|
||||||
import info.nightscout.androidaps.danars.di.DanaRSModule
|
import info.nightscout.androidaps.danars.di.DanaRSModule
|
||||||
import info.nightscout.androidaps.database.DatabaseModule
|
import info.nightscout.androidaps.database.DatabaseModule
|
||||||
|
import info.nightscout.androidaps.dependencyInjection.AutotuneModule
|
||||||
import info.nightscout.androidaps.diaconn.di.DiaconnG8Module
|
import info.nightscout.androidaps.diaconn.di.DiaconnG8Module
|
||||||
import info.nightscout.androidaps.insight.di.InsightDatabaseModule
|
import info.nightscout.androidaps.insight.di.InsightDatabaseModule
|
||||||
import info.nightscout.androidaps.insight.di.InsightModule
|
import info.nightscout.androidaps.insight.di.InsightModule
|
||||||
|
@ -37,6 +38,7 @@ import javax.inject.Singleton
|
||||||
ReceiversModule::class,
|
ReceiversModule::class,
|
||||||
ServicesModule::class,
|
ServicesModule::class,
|
||||||
AutomationModule::class,
|
AutomationModule::class,
|
||||||
|
AutotuneModule::class,
|
||||||
CommandQueueModule::class,
|
CommandQueueModule::class,
|
||||||
ObjectivesModule::class,
|
ObjectivesModule::class,
|
||||||
WizardModule::class,
|
WizardModule::class,
|
||||||
|
|
|
@ -15,6 +15,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
|
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
|
||||||
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
|
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
|
||||||
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation
|
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotunePlugin
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl
|
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
|
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
|
||||||
import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation
|
import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation
|
||||||
|
@ -98,6 +99,7 @@ open class AppModule {
|
||||||
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs
|
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs
|
||||||
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
|
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
|
||||||
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): Loop
|
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): Loop
|
||||||
|
@Binds fun bindAutotuneInterface(autotunePlugin: AutotunePlugin): Autotune
|
||||||
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
|
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
|
||||||
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
|
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
|
||||||
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
|
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package info.nightscout.androidaps.dependencyInjection
|
||||||
|
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.android.ContributesAndroidInjector
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotuneCore
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotuneIob
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotunePrep
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotuneFS
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.*
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@Suppress("unused")
|
||||||
|
abstract class AutotuneModule {
|
||||||
|
@ContributesAndroidInjector abstract fun autoTunePrepInjector(): AutotunePrep
|
||||||
|
@ContributesAndroidInjector abstract fun autoTuneIobInjector(): AutotuneIob
|
||||||
|
@ContributesAndroidInjector abstract fun autoTuneCoreInjector(): AutotuneCore
|
||||||
|
@ContributesAndroidInjector abstract fun autoTuneFSInjector(): AutotuneFS
|
||||||
|
|
||||||
|
@ContributesAndroidInjector abstract fun autoTuneATProfileInjector(): ATProfile
|
||||||
|
@ContributesAndroidInjector abstract fun autoTuneBGDatumInjector(): BGDatum
|
||||||
|
@ContributesAndroidInjector abstract fun autoTuneCRDatumInjector(): CRDatum
|
||||||
|
@ContributesAndroidInjector abstract fun autoTunePreppedGlucoseInjector(): PreppedGlucose
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragm
|
||||||
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
|
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
|
||||||
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment
|
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment
|
||||||
import info.nightscout.androidaps.plugins.general.automation.AutomationFragment
|
import info.nightscout.androidaps.plugins.general.automation.AutomationFragment
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotuneFragment
|
||||||
import info.nightscout.androidaps.plugins.general.food.FoodFragment
|
import info.nightscout.androidaps.plugins.general.food.FoodFragment
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment
|
import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment
|
||||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment
|
import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment
|
||||||
|
@ -36,6 +37,7 @@ abstract class FragmentsModule {
|
||||||
|
|
||||||
@ContributesAndroidInjector abstract fun contributesActionsFragment(): ActionsFragment
|
@ContributesAndroidInjector abstract fun contributesActionsFragment(): ActionsFragment
|
||||||
@ContributesAndroidInjector abstract fun contributesAutomationFragment(): AutomationFragment
|
@ContributesAndroidInjector abstract fun contributesAutomationFragment(): AutomationFragment
|
||||||
|
@ContributesAndroidInjector abstract fun contributesAutotuneFragment(): AutotuneFragment
|
||||||
@ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment
|
@ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment
|
||||||
|
|
||||||
@ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
|
@ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
|
||||||
|
|
|
@ -25,6 +25,7 @@ import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintP
|
||||||
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerPlugin
|
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerPlugin
|
||||||
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin
|
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin
|
||||||
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.AutotunePlugin
|
||||||
import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastPlugin
|
import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastPlugin
|
||||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
|
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
|
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
|
||||||
|
@ -232,6 +233,12 @@ abstract class PluginsModule {
|
||||||
@IntKey(250)
|
@IntKey(250)
|
||||||
abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase
|
abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@AllConfigs
|
||||||
|
@IntoMap
|
||||||
|
@IntKey(255)
|
||||||
|
abstract fun bindAutotunePlugin(plugin: AutotunePlugin): PluginBase
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@AllConfigs
|
@AllConfigs
|
||||||
@IntoMap
|
@IntoMap
|
||||||
|
|
|
@ -0,0 +1,516 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.LocalInsulin
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||||
|
import info.nightscout.androidaps.utils.Round
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AutotuneCore @Inject constructor(
|
||||||
|
private val sp: SP,
|
||||||
|
private val autotuneFS: AutotuneFS
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun tuneAllTheThings(preppedGlucose: PreppedGlucose, previousAutotune: ATProfile, pumpProfile: ATProfile): ATProfile {
|
||||||
|
//var pumpBasalProfile = pumpProfile.basalprofile;
|
||||||
|
val pumpBasalProfile = pumpProfile.basal
|
||||||
|
//console.error(pumpBasalProfile);
|
||||||
|
var basalProfile = previousAutotune.basal
|
||||||
|
//console.error(basalProfile);
|
||||||
|
//console.error(isfProfile);
|
||||||
|
var isf = previousAutotune.isf
|
||||||
|
//console.error(isf);
|
||||||
|
var carbRatio = previousAutotune.ic
|
||||||
|
//console.error(carbRatio);
|
||||||
|
var csf = isf / carbRatio
|
||||||
|
var dia = previousAutotune.dia
|
||||||
|
var peak = previousAutotune.peak
|
||||||
|
val csfGlucose = preppedGlucose.csfGlucoseData
|
||||||
|
val isfGlucose = preppedGlucose.isfGlucoseData
|
||||||
|
val basalGlucose = preppedGlucose.basalGlucoseData
|
||||||
|
val crData = preppedGlucose.crData
|
||||||
|
val diaDeviations = preppedGlucose.diaDeviations
|
||||||
|
val peakDeviations = preppedGlucose.peakDeviations
|
||||||
|
val pumpISF = pumpProfile.isf
|
||||||
|
val pumpCarbRatio = pumpProfile.ic
|
||||||
|
val pumpCSF = pumpISF / pumpCarbRatio
|
||||||
|
// Autosens constraints
|
||||||
|
val autotuneMax = sp.getDouble(R.string.key_openapsama_autosens_max, 1.2)
|
||||||
|
val autotuneMin = sp.getDouble(R.string.key_openapsama_autosens_min, 0.7)
|
||||||
|
val min5minCarbImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, 3.0)
|
||||||
|
|
||||||
|
// tune DIA
|
||||||
|
var newDia = dia
|
||||||
|
if (diaDeviations.size > 0)
|
||||||
|
{
|
||||||
|
val currentDiaMeanDev = diaDeviations[2].meanDeviation
|
||||||
|
val currentDiaRMSDev = diaDeviations[2].rmsDeviation
|
||||||
|
//Console.WriteLine(DIA,currentDIAMeanDev,currentDIARMSDev);
|
||||||
|
var minMeanDeviations = 1000000.0
|
||||||
|
var minRmsDeviations = 1000000.0
|
||||||
|
var meanBest = 2
|
||||||
|
var rmsBest = 2
|
||||||
|
for (i in 0..diaDeviations.size-1)
|
||||||
|
{
|
||||||
|
val meanDeviations = diaDeviations[i].meanDeviation
|
||||||
|
val rmsDeviations = diaDeviations[i].rmsDeviation
|
||||||
|
if (meanDeviations < minMeanDeviations)
|
||||||
|
{
|
||||||
|
minMeanDeviations = Round.roundTo(meanDeviations, 0.001)
|
||||||
|
meanBest = i
|
||||||
|
}
|
||||||
|
if (rmsDeviations < minRmsDeviations)
|
||||||
|
{
|
||||||
|
minRmsDeviations = Round.roundTo(rmsDeviations, 0.001)
|
||||||
|
rmsBest = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("Best insulinEndTime for meanDeviations: ${diaDeviations[meanBest].dia} hours")
|
||||||
|
log("Best insulinEndTime for RMSDeviations: ${diaDeviations[rmsBest].dia} hours")
|
||||||
|
if (meanBest < 2 && rmsBest < 2)
|
||||||
|
{
|
||||||
|
if (diaDeviations[1].meanDeviation < currentDiaMeanDev * 0.99 && diaDeviations[1].rmsDeviation < currentDiaRMSDev * 0.99)
|
||||||
|
newDia = diaDeviations[1].dia
|
||||||
|
}
|
||||||
|
else if (meanBest > 2 && rmsBest > 2)
|
||||||
|
{
|
||||||
|
if (diaDeviations[3].meanDeviation < currentDiaMeanDev * 0.99 && diaDeviations[3].rmsDeviation < currentDiaRMSDev * 0.99)
|
||||||
|
newDia = diaDeviations[3].dia
|
||||||
|
}
|
||||||
|
if (newDia > 12.0)
|
||||||
|
{
|
||||||
|
log("insulinEndTime maximum is 12h: not raising further")
|
||||||
|
newDia = 12.0
|
||||||
|
}
|
||||||
|
if (newDia != dia)
|
||||||
|
log("Adjusting insulinEndTime from $dia to $newDia hours")
|
||||||
|
else
|
||||||
|
log("Leaving insulinEndTime unchanged at $dia hours")
|
||||||
|
}
|
||||||
|
|
||||||
|
// tune insulinPeakTime
|
||||||
|
var newPeak = peak
|
||||||
|
if (peakDeviations.size > 2)
|
||||||
|
{
|
||||||
|
val currentPeakMeanDev = peakDeviations[2].meanDeviation
|
||||||
|
val currentPeakRMSDev = peakDeviations[2].rmsDeviation
|
||||||
|
//Console.WriteLine(currentPeakMeanDev);
|
||||||
|
var minMeanDeviations = 1000000.0
|
||||||
|
var minRmsDeviations = 1000000.0
|
||||||
|
var meanBest = 2
|
||||||
|
var rmsBest = 2
|
||||||
|
for (i in 0..peakDeviations.size-1)
|
||||||
|
{
|
||||||
|
val meanDeviations = peakDeviations[i].meanDeviation;
|
||||||
|
val rmsDeviations = peakDeviations[i].rmsDeviation;
|
||||||
|
if (meanDeviations < minMeanDeviations)
|
||||||
|
{
|
||||||
|
minMeanDeviations = Round.roundTo(meanDeviations, 0.001)
|
||||||
|
meanBest = i
|
||||||
|
}
|
||||||
|
if (rmsDeviations < minRmsDeviations)
|
||||||
|
{
|
||||||
|
minRmsDeviations = Round.roundTo(rmsDeviations, 0.001)
|
||||||
|
rmsBest = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("Best insulinPeakTime for meanDeviations: ${peakDeviations[meanBest].peak} minutes")
|
||||||
|
log("Best insulinPeakTime for RMSDeviations: ${peakDeviations[rmsBest].peak} minutes")
|
||||||
|
if (meanBest < 2 && rmsBest < 2)
|
||||||
|
{
|
||||||
|
if (peakDeviations[1].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[1].rmsDeviation < currentPeakRMSDev * 0.99)
|
||||||
|
newPeak = peakDeviations[1].peak
|
||||||
|
}
|
||||||
|
else if (meanBest > 2 && rmsBest > 2)
|
||||||
|
{
|
||||||
|
if (peakDeviations[3].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[3].rmsDeviation < currentPeakRMSDev * 0.99)
|
||||||
|
newPeak = peakDeviations[3].peak
|
||||||
|
}
|
||||||
|
if (newPeak != peak)
|
||||||
|
log("Adjusting insulinPeakTime from " + peak + " to " + newPeak + " minutes")
|
||||||
|
else
|
||||||
|
log("Leaving insulinPeakTime unchanged at " + peak)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate carb ratio (CR) independently of csf and isf
|
||||||
|
// Use the time period from meal bolus/carbs until COB is zero and IOB is < currentBasal/2
|
||||||
|
// For now, if another meal IOB/COB stacks on top of it, consider them together
|
||||||
|
// Compare beginning and ending BGs, and calculate how much more/less insulin is needed to neutralize
|
||||||
|
// Use entered carbs vs. starting IOB + delivered insulin + needed-at-end insulin to directly calculate CR.
|
||||||
|
|
||||||
|
//autotune-core (lib/autotune/index.js) #149-#165
|
||||||
|
var crTotalCarbs = 0.0
|
||||||
|
var crTotalInsulin = 0.0
|
||||||
|
for (i in crData.indices) {
|
||||||
|
val crDatum = crData[i]
|
||||||
|
val crBGChange = crDatum.crEndBG - crDatum.crInitialBG
|
||||||
|
val crInsulinReq = crBGChange / isf
|
||||||
|
//val crIOBChange = crDatum.crEndIOB - crDatum.crInitialIOB
|
||||||
|
crDatum.crInsulinTotal = crDatum.crInitialIOB + crDatum.crInsulin + crInsulinReq
|
||||||
|
//log(crDatum.crInitialIOB + " " + crDatum.crInsulin + " " + crInsulinReq + " " + crDatum.crInsulinTotal);
|
||||||
|
//val cr = Round.roundTo(crDatum.crCarbs / crDatum.crInsulinTotal, 0.001)
|
||||||
|
//log(crBGChange + " " + crInsulinReq + " " + crIOBChange + " " + crDatum.crInsulinTotal);
|
||||||
|
//log("CRCarbs: " + crDatum.crCarbs + " CRInsulin: " + crDatum.crInsulinTotal + " CR:" + cr);
|
||||||
|
if (crDatum.crInsulinTotal > 0) {
|
||||||
|
crTotalCarbs += crDatum.crCarbs
|
||||||
|
crTotalInsulin += crDatum.crInsulinTotal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//autotune-core (lib/autotune/index.js) #166-#169
|
||||||
|
crTotalInsulin = Round.roundTo(crTotalInsulin, 0.001)
|
||||||
|
var totalCR = 0.0
|
||||||
|
if (crTotalInsulin != 0.0)
|
||||||
|
totalCR = Round.roundTo(crTotalCarbs / crTotalInsulin, 0.001)
|
||||||
|
log("crTotalCarbs: $crTotalCarbs crTotalInsulin: $crTotalInsulin totalCR: $totalCR")
|
||||||
|
|
||||||
|
//autotune-core (lib/autotune/index.js) #170-#209 (already hourly in aaps)
|
||||||
|
// convert the basal profile to hourly if it isn't already
|
||||||
|
val hourlyBasalProfile = basalProfile
|
||||||
|
|
||||||
|
//log(hourlyPumpProfile.toString());
|
||||||
|
//log(hourlyBasalProfile.toString());
|
||||||
|
val newHourlyBasalProfile = DoubleArray(24)
|
||||||
|
for (i in 0..23) {
|
||||||
|
newHourlyBasalProfile[i] = hourlyBasalProfile[i]
|
||||||
|
}
|
||||||
|
val basalUntuned = previousAutotune.basalUntuned
|
||||||
|
|
||||||
|
//autotune-core (lib/autotune/index.js) #210-#266
|
||||||
|
// look at net deviations for each hour
|
||||||
|
for (hour in 0..23) {
|
||||||
|
var deviations = 0.0
|
||||||
|
for (i in basalGlucose.indices) {
|
||||||
|
val BGTime = Calendar.getInstance()
|
||||||
|
//var BGTime: Date? = null
|
||||||
|
if (basalGlucose[i].date != 0L) {
|
||||||
|
BGTime.setTimeInMillis(basalGlucose[i].date)
|
||||||
|
//BGTime = Date(basalGlucose[i].date)
|
||||||
|
} else {
|
||||||
|
log("Could not determine last BG time")
|
||||||
|
}
|
||||||
|
val myHour = BGTime.get(Calendar.HOUR_OF_DAY)
|
||||||
|
//val myHour = BGTime!!.hours
|
||||||
|
if (hour == myHour) {
|
||||||
|
//log.debug(basalGlucose[i].deviation);
|
||||||
|
deviations += basalGlucose[i].deviation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deviations = Round.roundTo(deviations, 0.001)
|
||||||
|
log("Hour $hour total deviations: $deviations mg/dL")
|
||||||
|
// calculate how much less or additional basal insulin would have been required to eliminate the deviations
|
||||||
|
// only apply 20% of the needed adjustment to keep things relatively stable
|
||||||
|
var basalNeeded = 0.2 * deviations / isf
|
||||||
|
basalNeeded = Round.roundTo(basalNeeded, 0.01)
|
||||||
|
// if basalNeeded is positive, adjust each of the 1-3 hour prior basals by 10% of the needed adjustment
|
||||||
|
log("Hour $hour basal adjustment needed: $basalNeeded U/hr")
|
||||||
|
if (basalNeeded > 0) {
|
||||||
|
for (offset in -3..-1) {
|
||||||
|
var offsetHour = hour + offset
|
||||||
|
if (offsetHour < 0) {
|
||||||
|
offsetHour += 24
|
||||||
|
}
|
||||||
|
//log.debug(offsetHour);
|
||||||
|
newHourlyBasalProfile[offsetHour] = newHourlyBasalProfile[offsetHour] + basalNeeded / 3
|
||||||
|
newHourlyBasalProfile[offsetHour] = Round.roundTo(newHourlyBasalProfile[offsetHour], 0.001)
|
||||||
|
}
|
||||||
|
// otherwise, figure out the percentage reduction required to the 1-3 hour prior basals
|
||||||
|
// and adjust all of them downward proportionally
|
||||||
|
} else if (basalNeeded < 0) {
|
||||||
|
var threeHourBasal = 0.0
|
||||||
|
for (offset in -3..-1) {
|
||||||
|
var offsetHour = hour + offset
|
||||||
|
if (offsetHour < 0) {
|
||||||
|
offsetHour += 24
|
||||||
|
}
|
||||||
|
threeHourBasal += newHourlyBasalProfile[offsetHour]
|
||||||
|
}
|
||||||
|
val adjustmentRatio = 1.0 + basalNeeded / threeHourBasal
|
||||||
|
//log.debug(adjustmentRatio);
|
||||||
|
for (offset in -3..-1) {
|
||||||
|
var offsetHour = hour + offset
|
||||||
|
if (offsetHour < 0) {
|
||||||
|
offsetHour += 24
|
||||||
|
}
|
||||||
|
newHourlyBasalProfile[offsetHour] = newHourlyBasalProfile[offsetHour] * adjustmentRatio
|
||||||
|
newHourlyBasalProfile[offsetHour] = Round.roundTo(newHourlyBasalProfile[offsetHour], 0.001)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//autotune-core (lib/autotune/index.js) #267-#294
|
||||||
|
for (hour in 0..23) {
|
||||||
|
//log.debug(newHourlyBasalProfile[hour],hourlyPumpProfile[hour].rate*1.2);
|
||||||
|
// cap adjustments at autosens_max and autosens_min
|
||||||
|
val maxRate = pumpBasalProfile[hour] * autotuneMax
|
||||||
|
val minRate = pumpBasalProfile[hour] * autotuneMin
|
||||||
|
if (newHourlyBasalProfile[hour] > maxRate) {
|
||||||
|
log("Limiting hour " + hour + " basal to " + Round.roundTo(maxRate, 0.01) + " (which is " + Round.roundTo(autotuneMax, 0.01) + " * pump basal of " + pumpBasalProfile[hour] + ")")
|
||||||
|
//log.debug("Limiting hour",hour,"basal to",maxRate.toFixed(2),"(which is 20% above pump basal of",hourlyPumpProfile[hour].rate,")");
|
||||||
|
newHourlyBasalProfile[hour] = maxRate
|
||||||
|
} else if (newHourlyBasalProfile[hour] < minRate) {
|
||||||
|
log("Limiting hour " + hour + " basal to " + Round.roundTo(minRate, 0.01) + " (which is " + autotuneMin + " * pump basal of " + newHourlyBasalProfile[hour] + ")")
|
||||||
|
//log.debug("Limiting hour",hour,"basal to",minRate.toFixed(2),"(which is 20% below pump basal of",hourlyPumpProfile[hour].rate,")");
|
||||||
|
newHourlyBasalProfile[hour] = minRate
|
||||||
|
}
|
||||||
|
newHourlyBasalProfile[hour] = Round.roundTo(newHourlyBasalProfile[hour], 0.001)
|
||||||
|
}
|
||||||
|
|
||||||
|
// some hours of the day rarely have data to tune basals due to meals.
|
||||||
|
// when no adjustments are needed to a particular hour, we should adjust it toward the average of the
|
||||||
|
// periods before and after it that do have data to be tuned
|
||||||
|
var lastAdjustedHour = 0
|
||||||
|
// scan through newHourlyBasalProfile and find hours where the rate is unchanged
|
||||||
|
//autotune-core (lib/autotune/index.js) #302-#323
|
||||||
|
for (hour in 0..23) {
|
||||||
|
if (hourlyBasalProfile[hour] == newHourlyBasalProfile[hour]) {
|
||||||
|
var nextAdjustedHour = 23
|
||||||
|
for (nextHour in hour..23) {
|
||||||
|
if (hourlyBasalProfile[nextHour] != newHourlyBasalProfile[nextHour]) {
|
||||||
|
nextAdjustedHour = nextHour
|
||||||
|
break
|
||||||
|
//} else {
|
||||||
|
// log("At hour: "+nextHour +" " + hourlyBasalProfile[nextHour] + " " +newHourlyBasalProfile[nextHour]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//log.debug(hour, newHourlyBasalProfile);
|
||||||
|
newHourlyBasalProfile[hour] = Round.roundTo(0.8 * hourlyBasalProfile[hour] + 0.1 * newHourlyBasalProfile[lastAdjustedHour] + 0.1 * newHourlyBasalProfile[nextAdjustedHour], 0.001)
|
||||||
|
basalUntuned[hour]++
|
||||||
|
log("Adjusting hour " + hour + " basal from " + hourlyBasalProfile[hour] + " to " + newHourlyBasalProfile[hour] + " based on hour " + lastAdjustedHour + " = " + newHourlyBasalProfile[lastAdjustedHour] + " and hour " + nextAdjustedHour + " = " + newHourlyBasalProfile[nextAdjustedHour])
|
||||||
|
} else {
|
||||||
|
lastAdjustedHour = hour
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//log(newHourlyBasalProfile.toString());
|
||||||
|
basalProfile = newHourlyBasalProfile
|
||||||
|
|
||||||
|
// Calculate carb ratio (CR) independently of csf and isf
|
||||||
|
// Use the time period from meal bolus/carbs until COB is zero and IOB is < currentBasal/2
|
||||||
|
// For now, if another meal IOB/COB stacks on top of it, consider them together
|
||||||
|
// Compare beginning and ending BGs, and calculate how much more/less insulin is needed to neutralize
|
||||||
|
// Use entered carbs vs. starting IOB + delivered insulin + needed-at-end insulin to directly calculate CR.
|
||||||
|
|
||||||
|
// calculate net deviations while carbs are absorbing
|
||||||
|
// measured from carb entry until COB and deviations both drop to zero
|
||||||
|
var deviations = 0.0
|
||||||
|
var mealCarbs = 0
|
||||||
|
var totalMealCarbs = 0
|
||||||
|
var totalDeviations = 0.0
|
||||||
|
val fullNewCSF: Double
|
||||||
|
//log.debug(CSFGlucose[0].mealAbsorption);
|
||||||
|
//log.debug(CSFGlucose[0]);
|
||||||
|
//autotune-core (lib/autotune/index.js) #346-#365
|
||||||
|
for (i in csfGlucose.indices) {
|
||||||
|
//log.debug(CSFGlucose[i].mealAbsorption, i);
|
||||||
|
if (csfGlucose[i].mealAbsorption === "start") {
|
||||||
|
deviations = 0.0
|
||||||
|
mealCarbs = csfGlucose[i].mealCarbs
|
||||||
|
} else if (csfGlucose[i].mealAbsorption === "end") {
|
||||||
|
deviations += csfGlucose[i].deviation
|
||||||
|
// compare the sum of deviations from start to end vs. current csf * mealCarbs
|
||||||
|
//log.debug(csf,mealCarbs);
|
||||||
|
//val csfRise = csf * mealCarbs
|
||||||
|
//log.debug(deviations,isf);
|
||||||
|
//log.debug("csfRise:",csfRise,"deviations:",deviations);
|
||||||
|
totalMealCarbs += mealCarbs
|
||||||
|
totalDeviations += deviations
|
||||||
|
} else {
|
||||||
|
//todo Philoul check 0 * min5minCarbImpact ???
|
||||||
|
deviations += Math.max(0 * min5minCarbImpact, csfGlucose[i].deviation)
|
||||||
|
mealCarbs = Math.max(mealCarbs, csfGlucose[i].mealCarbs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// at midnight, write down the mealcarbs as total meal carbs (to prevent special case of when only one meal and it not finishing absorbing by midnight)
|
||||||
|
// TODO: figure out what to do with dinner carbs that don't finish absorbing by midnight
|
||||||
|
if (totalMealCarbs == 0) {
|
||||||
|
totalMealCarbs += mealCarbs
|
||||||
|
}
|
||||||
|
if (totalDeviations == 0.0) {
|
||||||
|
totalDeviations += deviations
|
||||||
|
}
|
||||||
|
//log.debug(totalDeviations, totalMealCarbs);
|
||||||
|
fullNewCSF = if (totalMealCarbs == 0) {
|
||||||
|
// if no meals today, csf is unchanged
|
||||||
|
csf
|
||||||
|
} else {
|
||||||
|
// how much change would be required to account for all of the deviations
|
||||||
|
Round.roundTo(totalDeviations / totalMealCarbs, 0.01)
|
||||||
|
}
|
||||||
|
// only adjust by 20%
|
||||||
|
var newCSF = 0.8 * csf + 0.2 * fullNewCSF
|
||||||
|
// safety cap csf
|
||||||
|
if (pumpCSF != 0.0) {
|
||||||
|
val maxCSF = pumpCSF * autotuneMax
|
||||||
|
val minCSF = pumpCSF * autotuneMin
|
||||||
|
if (newCSF > maxCSF) {
|
||||||
|
log("Limiting csf to " + Round.roundTo(maxCSF, 0.01) + " (which is " + autotuneMax + "* pump csf of " + pumpCSF + ")")
|
||||||
|
newCSF = maxCSF
|
||||||
|
} else if (newCSF < minCSF) {
|
||||||
|
log("Limiting csf to " + Round.roundTo(minCSF, 0.01) + " (which is" + autotuneMin + "* pump csf of " + pumpCSF + ")")
|
||||||
|
newCSF = minCSF
|
||||||
|
} //else { log.debug("newCSF",newCSF,"is close enough to",pumpCSF); }
|
||||||
|
}
|
||||||
|
val oldCSF = Round.roundTo(csf, 0.001)
|
||||||
|
newCSF = Round.roundTo(newCSF, 0.001)
|
||||||
|
totalDeviations = Round.roundTo(totalDeviations, 0.001)
|
||||||
|
log("totalMealCarbs: $totalMealCarbs totalDeviations: $totalDeviations oldCSF $oldCSF fullNewCSF: $fullNewCSF newCSF: $newCSF")
|
||||||
|
// this is where csf is set based on the outputs
|
||||||
|
//if (newCSF != 0.0) {
|
||||||
|
// csf = newCSF
|
||||||
|
//}
|
||||||
|
var fullNewCR: Double
|
||||||
|
fullNewCR = if (totalCR == 0.0) {
|
||||||
|
// if no meals today, CR is unchanged
|
||||||
|
carbRatio
|
||||||
|
} else {
|
||||||
|
// how much change would be required to account for all of the deviations
|
||||||
|
totalCR
|
||||||
|
}
|
||||||
|
// don't tune CR out of bounds
|
||||||
|
var maxCR = pumpCarbRatio * autotuneMax
|
||||||
|
if (maxCR > 150) {
|
||||||
|
maxCR = 150.0
|
||||||
|
}
|
||||||
|
var minCR = pumpCarbRatio * autotuneMin
|
||||||
|
if (minCR < 3) {
|
||||||
|
minCR = 3.0
|
||||||
|
}
|
||||||
|
// safety cap fullNewCR
|
||||||
|
if (pumpCarbRatio != 0.0) {
|
||||||
|
if (fullNewCR > maxCR) {
|
||||||
|
log("Limiting fullNewCR from " + fullNewCR + " to " + Round.roundTo(maxCR, 0.01) + " (which is " + autotuneMax + " * pump CR of " + pumpCarbRatio + ")")
|
||||||
|
fullNewCR = maxCR
|
||||||
|
} else if (fullNewCR < minCR) {
|
||||||
|
log("Limiting fullNewCR from " + fullNewCR + " to " + Round.roundTo(minCR, 0.01) + " (which is " + autotuneMin + " * pump CR of " + pumpCarbRatio + ")")
|
||||||
|
fullNewCR = minCR
|
||||||
|
} //else { log.debug("newCR",newCR,"is close enough to",pumpCarbRatio); }
|
||||||
|
}
|
||||||
|
// only adjust by 20%
|
||||||
|
var newCR = 0.8 * carbRatio + 0.2 * fullNewCR
|
||||||
|
// safety cap newCR
|
||||||
|
if (pumpCarbRatio != 0.0) {
|
||||||
|
if (newCR > maxCR) {
|
||||||
|
log("Limiting CR to " + Round.roundTo(maxCR, 0.01) + " (which is " + autotuneMax + " * pump CR of " + pumpCarbRatio + ")")
|
||||||
|
newCR = maxCR
|
||||||
|
} else if (newCR < minCR) {
|
||||||
|
log("Limiting CR to " + Round.roundTo(minCR, 0.01) + " (which is " + autotuneMin + " * pump CR of " + pumpCarbRatio + ")")
|
||||||
|
newCR = minCR
|
||||||
|
} //else { log.debug("newCR",newCR,"is close enough to",pumpCarbRatio); }
|
||||||
|
}
|
||||||
|
newCR = Round.roundTo(newCR, 0.001)
|
||||||
|
log("oldCR: $carbRatio fullNewCR: $fullNewCR newCR: $newCR")
|
||||||
|
// this is where CR is set based on the outputs
|
||||||
|
//var ISFFromCRAndCSF = isf;
|
||||||
|
if (newCR != 0.0) {
|
||||||
|
carbRatio = newCR
|
||||||
|
//ISFFromCRAndCSF = Math.round( carbRatio * csf * 1000)/1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate median deviation and bgi in data attributable to isf
|
||||||
|
val isfDeviations: MutableList<Double> = ArrayList()
|
||||||
|
val bGIs: MutableList<Double> = ArrayList()
|
||||||
|
val avgDeltas: MutableList<Double> = ArrayList()
|
||||||
|
val ratios: MutableList<Double> = ArrayList()
|
||||||
|
var count = 0
|
||||||
|
for (i in isfGlucose.indices) {
|
||||||
|
val deviation = isfGlucose[i].deviation
|
||||||
|
isfDeviations.add(deviation)
|
||||||
|
val BGI = isfGlucose[i].bgi
|
||||||
|
bGIs.add(BGI)
|
||||||
|
val avgDelta = isfGlucose[i].avgDelta
|
||||||
|
avgDeltas.add(avgDelta)
|
||||||
|
val ratio = 1 + deviation / BGI
|
||||||
|
//log.debug("Deviation:",deviation,"BGI:",BGI,"avgDelta:",avgDelta,"ratio:",ratio);
|
||||||
|
ratios.add(ratio)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
Collections.sort(avgDeltas)
|
||||||
|
Collections.sort(bGIs)
|
||||||
|
Collections.sort(isfDeviations)
|
||||||
|
Collections.sort(ratios)
|
||||||
|
var p50deviation = IobCobCalculatorPlugin.percentile(isfDeviations.toTypedArray(), 0.50)
|
||||||
|
var p50BGI = IobCobCalculatorPlugin.percentile(bGIs.toTypedArray(), 0.50)
|
||||||
|
val p50ratios = Round.roundTo(IobCobCalculatorPlugin.percentile(ratios.toTypedArray(), 0.50), 0.001)
|
||||||
|
var fullNewISF = isf
|
||||||
|
if (count < 10) {
|
||||||
|
// leave isf unchanged if fewer than 5 isf data points
|
||||||
|
log("Only found " + isfGlucose.size + " ISF data points, leaving ISF unchanged at " + isf)
|
||||||
|
} else {
|
||||||
|
// calculate what adjustments to isf would have been necessary to bring median deviation to zero
|
||||||
|
fullNewISF = isf * p50ratios
|
||||||
|
}
|
||||||
|
fullNewISF = Round.roundTo(fullNewISF, 0.001)
|
||||||
|
// adjust the target isf to be a weighted average of fullNewISF and pumpISF
|
||||||
|
val adjustmentFraction: Double
|
||||||
|
/*
|
||||||
|
// TODO: philoul may be allow adjustmentFraction in settings with safety limits ?)
|
||||||
|
if (typeof(pumpProfile.autotune_isf_adjustmentFraction) !== 'undefined') {
|
||||||
|
adjustmentFraction = pumpProfile.autotune_isf_adjustmentFraction;
|
||||||
|
} else {*/
|
||||||
|
adjustmentFraction = 1.0
|
||||||
|
// }
|
||||||
|
|
||||||
|
// low autosens ratio = high isf
|
||||||
|
val maxISF = pumpISF / autotuneMin
|
||||||
|
// high autosens ratio = low isf
|
||||||
|
val minISF = pumpISF / autotuneMax
|
||||||
|
var adjustedISF = 0.0
|
||||||
|
var newISF = 0.0
|
||||||
|
if (pumpISF != 0.0) {
|
||||||
|
adjustedISF = if (fullNewISF < 0) {
|
||||||
|
isf
|
||||||
|
} else {
|
||||||
|
adjustmentFraction * fullNewISF + (1 - adjustmentFraction) * pumpISF
|
||||||
|
}
|
||||||
|
// cap adjustedISF before applying 10%
|
||||||
|
//log.debug(adjustedISF, maxISF, minISF);
|
||||||
|
if (adjustedISF > maxISF) {
|
||||||
|
log("Limiting adjusted isf of " + Round.roundTo(adjustedISF, 0.01) + " to " + Round.roundTo(maxISF, 0.01) + "(which is pump isf of " + pumpISF + "/" + autotuneMin + ")")
|
||||||
|
adjustedISF = maxISF
|
||||||
|
} else if (adjustedISF < minISF) {
|
||||||
|
log("Limiting adjusted isf of" + Round.roundTo(adjustedISF, 0.01) + " to " + Round.roundTo(minISF, 0.01) + "(which is pump isf of " + pumpISF + "/" + autotuneMax + ")")
|
||||||
|
adjustedISF = minISF
|
||||||
|
}
|
||||||
|
|
||||||
|
// and apply 20% of that adjustment
|
||||||
|
newISF = 0.8 * isf + 0.2 * adjustedISF
|
||||||
|
if (newISF > maxISF) {
|
||||||
|
log("Limiting isf of" + Round.roundTo(newISF, 0.01) + "to" + Round.roundTo(maxISF, 0.01) + "(which is pump isf of" + pumpISF + "/" + autotuneMin + ")")
|
||||||
|
newISF = maxISF
|
||||||
|
} else if (newISF < minISF) {
|
||||||
|
log("Limiting isf of" + Round.roundTo(newISF, 0.01) + "to" + Round.roundTo(minISF, 0.01) + "(which is pump isf of" + pumpISF + "/" + autotuneMax + ")")
|
||||||
|
newISF = minISF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newISF = Round.roundTo(newISF, 0.001)
|
||||||
|
//log.debug(avgRatio);
|
||||||
|
//log.debug(newISF);
|
||||||
|
p50deviation = Round.roundTo(p50deviation, 0.001)
|
||||||
|
p50BGI = Round.roundTo(p50BGI, 0.001)
|
||||||
|
adjustedISF = Round.roundTo(adjustedISF, 0.001)
|
||||||
|
log("p50deviation: $p50deviation p50BGI $p50BGI p50ratios: $p50ratios Old isf: $isf fullNewISF: $fullNewISF adjustedISF: $adjustedISF newISF: $newISF")
|
||||||
|
if (newISF != 0.0) {
|
||||||
|
isf = newISF
|
||||||
|
}
|
||||||
|
previousAutotune.from = preppedGlucose.from
|
||||||
|
previousAutotune.basal = basalProfile
|
||||||
|
previousAutotune.isf = isf
|
||||||
|
previousAutotune.ic = Round.roundTo(carbRatio, 0.001)
|
||||||
|
previousAutotune.basalUntuned = basalUntuned
|
||||||
|
previousAutotune.dia = newDia
|
||||||
|
previousAutotune.peak = newPeak
|
||||||
|
val localInsulin = LocalInsulin("Ins_$newPeak-$newDia", newPeak, newDia)
|
||||||
|
previousAutotune.localInsulin = localInsulin
|
||||||
|
previousAutotune.updateProfile()
|
||||||
|
return previousAutotune
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log(message: String) {
|
||||||
|
autotuneFS.atLog("[Core] $message")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
|
||||||
|
import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AutotuneFS @Inject constructor(
|
||||||
|
private val rh: ResourceHelper,
|
||||||
|
private val loggerUtils: LoggerUtils
|
||||||
|
) {
|
||||||
|
|
||||||
|
val AUTOTUNEFOLDER = "autotune"
|
||||||
|
val SETTINGSFOLDER = "settings"
|
||||||
|
val RECOMMENDATIONS = "autotune_recommendations.log"
|
||||||
|
val ENTRIESPREF = "aaps-entries."
|
||||||
|
val TREATMENTSPREF = "aaps-treatments."
|
||||||
|
val AAPSBOLUSESPREF = "aaps-boluses."
|
||||||
|
val PREPPEDPREF = "aaps-autotune."
|
||||||
|
val SETTINGS = "settings.json"
|
||||||
|
val PROFIL = "profil"
|
||||||
|
val PUMPPROFILE = "pumpprofile.json"
|
||||||
|
val TUNEDPROFILE = "newaapsprofile."
|
||||||
|
val LOGPREF = "autotune."
|
||||||
|
val ZIPPREF = "autotune_"
|
||||||
|
lateinit var autotunePath: File
|
||||||
|
lateinit var autotuneSettings: File
|
||||||
|
private var logString = ""
|
||||||
|
val BUFFER_SIZE = 2048
|
||||||
|
private val log = LoggerFactory.getLogger(AutotunePlugin::class.java)
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Create autotune folder for all files created during an autotune session
|
||||||
|
*****************************************************************************/
|
||||||
|
fun createAutotuneFolder() {
|
||||||
|
//create autotune subfolder for autotune files if not exists
|
||||||
|
autotunePath = File(loggerUtils.logDirectory, AUTOTUNEFOLDER)
|
||||||
|
if (!(autotunePath.exists() && autotunePath.isDirectory)) {
|
||||||
|
autotunePath.mkdir()
|
||||||
|
log("Create $AUTOTUNEFOLDER subfolder in ${loggerUtils.logDirectory}")
|
||||||
|
}
|
||||||
|
autotuneSettings = File(loggerUtils.logDirectory, SETTINGSFOLDER)
|
||||||
|
if (!(autotuneSettings.exists() && autotuneSettings.isDirectory)) {
|
||||||
|
autotuneSettings.mkdir()
|
||||||
|
log("Create $SETTINGSFOLDER subfolder in ${loggerUtils.logDirectory}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* between each run of autotune, clean autotune folder content
|
||||||
|
*****************************************************************************/
|
||||||
|
fun deleteAutotuneFiles() {
|
||||||
|
autotunePath.listFiles()?.let { listFiles ->
|
||||||
|
for (file in listFiles) {
|
||||||
|
if (file.isFile) file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
autotuneSettings.listFiles()?.let { listFiles ->
|
||||||
|
for (file in listFiles) {
|
||||||
|
if (file.isFile) file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("Delete previous Autotune files")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Create a JSON autotune files or settings files
|
||||||
|
*****************************************************************************/
|
||||||
|
fun exportSettings(settings: String) {
|
||||||
|
createAutotunefile(SETTINGS, settings, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportPumpProfile(profile: ATProfile) {
|
||||||
|
createAutotunefile(PUMPPROFILE, profile.profiletoOrefJSON(), true)
|
||||||
|
createAutotunefile(PUMPPROFILE, profile.profiletoOrefJSON())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportTunedProfile(tunedProfile: ATProfile) {
|
||||||
|
createAutotunefile(TUNEDPROFILE + formatDate(tunedProfile.from) + ".json", tunedProfile.profiletoOrefJSON())
|
||||||
|
try {
|
||||||
|
createAutotunefile(rh.gs(R.string.autotune_tunedprofile_name) + ".json", tunedProfile.profiletoOrefJSON(), true)
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportEntries(autotuneIob: AutotuneIob) {
|
||||||
|
try {
|
||||||
|
createAutotunefile(ENTRIESPREF + formatDate(autotuneIob.startBG) + ".json", autotuneIob.glucoseToJSON())
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportTreatments(autotuneIob: AutotuneIob) {
|
||||||
|
try {
|
||||||
|
createAutotunefile(TREATMENTSPREF + formatDate(autotuneIob.startBG) + ".json", autotuneIob.nsHistoryToJSON())
|
||||||
|
createAutotunefile(AAPSBOLUSESPREF + formatDate(autotuneIob.startBG) + ".json", autotuneIob.bolusesToJSON())
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportPreppedGlucose(preppedGlucose: PreppedGlucose) {
|
||||||
|
createAutotunefile(PREPPEDPREF + formatDate(preppedGlucose.from) + ".json", preppedGlucose.toString(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportResult(result: String) {
|
||||||
|
createAutotunefile(RECOMMENDATIONS, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportLog(lastRun: Long, index: Int = 0) {
|
||||||
|
val suffix = if (index == 0) "" else "_" + index
|
||||||
|
log("Create " + LOGPREF + formatDate(lastRun) + suffix + ".log" + " file in " + AUTOTUNEFOLDER + " folder")
|
||||||
|
createAutotunefile(LOGPREF + formatDate(lastRun) + suffix + ".log", logString)
|
||||||
|
logString = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportLogAndZip(lastRun: Long) {
|
||||||
|
log("Create " + LOGPREF + formatDate(lastRun) + ".log" + " file in " + AUTOTUNEFOLDER + " folder")
|
||||||
|
createAutotunefile(LOGPREF + formatDate(lastRun) + ".log", logString)
|
||||||
|
zipAutotune(lastRun)
|
||||||
|
logString = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAutotunefile(fileName: String, stringFile: String, isSettingFile: Boolean = false) {
|
||||||
|
val autotuneFile = File(if (isSettingFile) autotuneSettings.absolutePath else autotunePath.absolutePath, fileName)
|
||||||
|
try {
|
||||||
|
val fw = FileWriter(autotuneFile)
|
||||||
|
val pw = PrintWriter(fw)
|
||||||
|
pw.println(stringFile)
|
||||||
|
pw.close()
|
||||||
|
fw.close()
|
||||||
|
log("Create " + fileName + " file in " + (if (isSettingFile) SETTINGSFOLDER else AUTOTUNEFOLDER) + " folder")
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
//log.error("Unhandled exception", e);
|
||||||
|
} catch (e: IOException) {
|
||||||
|
//log.error("Unhandled exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************************
|
||||||
|
* create a zip file with all autotune files and settings in autotune folder at the end of run
|
||||||
|
**********************************************************************************/
|
||||||
|
fun zipAutotune(lastRun: Long) {
|
||||||
|
try {
|
||||||
|
val zipFileName = ZIPPREF + formatDate(lastRun, true) + ".zip"
|
||||||
|
val zipFile = File(loggerUtils.logDirectory, zipFileName)
|
||||||
|
val out = ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile)))
|
||||||
|
zipDirectory(autotunePath, autotunePath.name, out)
|
||||||
|
zipDirectory(autotuneSettings, autotuneSettings.name, out)
|
||||||
|
out.flush()
|
||||||
|
out.close()
|
||||||
|
log("Create $zipFileName file in ${loggerUtils.logDirectory} folder")
|
||||||
|
} catch (e: IOException) {
|
||||||
|
//log.error("Unhandled exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log(message: String) {
|
||||||
|
atLog("[FS] $message")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atLog(message: String) {
|
||||||
|
logString += "$message\n"
|
||||||
|
log.debug(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun zipDirectory(folder: File, parentFolder: String, out: ZipOutputStream) {
|
||||||
|
folder.listFiles()?.let { listFiles ->
|
||||||
|
for (file in listFiles) {
|
||||||
|
if (file.isDirectory) {
|
||||||
|
zipDirectory(file, parentFolder + "/" + file.name, out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
out.putNextEntry(ZipEntry(parentFolder + "/" + file.name))
|
||||||
|
val bis = BufferedInputStream(FileInputStream(file))
|
||||||
|
//long bytesRead = 0;
|
||||||
|
val bytesIn = ByteArray(BUFFER_SIZE)
|
||||||
|
var read: Int
|
||||||
|
while (bis.read(bytesIn).also { read = it } != -1) {
|
||||||
|
out.write(bytesIn, 0, read)
|
||||||
|
}
|
||||||
|
out.closeEntry()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
//log.error("Unhandled exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatDate(date: Long, dateHour: Boolean = false): String {
|
||||||
|
val dateFormat = if (dateHour) SimpleDateFormat("yyyy-MM-dd_HH-mm-ss") else SimpleDateFormat("yyyy-MM-dd")
|
||||||
|
return dateFormat.format(date)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,500 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.TableLayout
|
||||||
|
import android.widget.TableRow
|
||||||
|
import android.widget.TextView
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import dagger.android.support.DaggerFragment
|
||||||
|
import info.nightscout.androidaps.Constants
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.databinding.AutotuneFragmentBinding
|
||||||
|
import info.nightscout.androidaps.dialogs.ProfileViewerDialog
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui
|
||||||
|
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
|
||||||
|
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
|
||||||
|
import info.nightscout.androidaps.data.LocalInsulin
|
||||||
|
import info.nightscout.androidaps.data.ProfileSealed
|
||||||
|
import info.nightscout.androidaps.database.entities.UserEntry
|
||||||
|
import info.nightscout.androidaps.database.entities.ValueWithUnit
|
||||||
|
import info.nightscout.androidaps.extensions.runOnUiThread
|
||||||
|
import info.nightscout.androidaps.interfaces.*
|
||||||
|
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||||
|
import info.nightscout.androidaps.utils.MidnightTime
|
||||||
|
import info.nightscout.androidaps.utils.Round
|
||||||
|
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
|
||||||
|
import info.nightscout.shared.SafeParse
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||||
|
import org.json.JSONObject
|
||||||
|
//import org.slf4j.LoggerFactory
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AutotuneFragment : DaggerFragment() {
|
||||||
|
@Inject lateinit var profileFunction: ProfileFunction
|
||||||
|
@Inject lateinit var autotunePlugin: AutotunePlugin
|
||||||
|
@Inject lateinit var autotuneFS: AutotuneFS
|
||||||
|
@Inject lateinit var sp: SP
|
||||||
|
@Inject lateinit var dateUtil: DateUtil
|
||||||
|
@Inject lateinit var activePlugin: ActivePlugin
|
||||||
|
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
|
||||||
|
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||||
|
@Inject lateinit var uel: UserEntryLogger
|
||||||
|
@Inject lateinit var rh: ResourceHelper
|
||||||
|
@Inject lateinit var rxBus: RxBus
|
||||||
|
@Inject lateinit var injector: HasAndroidInjector
|
||||||
|
|
||||||
|
private var disposable: CompositeDisposable = CompositeDisposable()
|
||||||
|
//private val log = LoggerFactory.getLogger(AutotunePlugin::class.java)
|
||||||
|
private var _binding: AutotuneFragmentBinding? = null
|
||||||
|
private lateinit var profileStore: ProfileStore
|
||||||
|
private var profileName = ""
|
||||||
|
private lateinit var profile: ATProfile
|
||||||
|
// This property is only valid between onCreateView and
|
||||||
|
// onDestroyView.
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
_binding = AutotuneFragmentBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
autotunePlugin.lastRun = sp.getLong(R.string.key_autotune_last_run, 0)
|
||||||
|
if (autotunePlugin.lastNbDays.isEmpty())
|
||||||
|
autotunePlugin.lastNbDays = sp.getInt(R.string.key_autotune_default_tune_days, 5).toString()
|
||||||
|
val defaultValue = sp.getInt(R.string.key_autotune_default_tune_days, 5).toDouble()
|
||||||
|
profileStore = activePlugin.activeProfileSource.profile ?: ProfileStore(injector, JSONObject(), dateUtil)
|
||||||
|
profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString()
|
||||||
|
profileFunction.getProfile()?.let { currentProfile ->
|
||||||
|
profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?:currentProfile, LocalInsulin(""), injector)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.tuneDays.setParams(
|
||||||
|
savedInstanceState?.getDouble("tunedays")
|
||||||
|
?: defaultValue, 1.0, 30.0, 1.0, DecimalFormat("0"), false, null, textWatcher)
|
||||||
|
binding.autotuneRun.setOnClickListener {
|
||||||
|
val daysBack = SafeParse.stringToInt(binding.tuneDays.text)
|
||||||
|
autotunePlugin.calculationRunning = true
|
||||||
|
autotunePlugin.lastNbDays = daysBack.toString()
|
||||||
|
log("Run Autotune $profileName, $daysBack days")
|
||||||
|
Thread {
|
||||||
|
autotunePlugin.aapsAutotune(daysBack, false, profileName)
|
||||||
|
}.start()
|
||||||
|
updateGui()
|
||||||
|
}
|
||||||
|
binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ ->
|
||||||
|
if (!autotunePlugin.calculationRunning)
|
||||||
|
{
|
||||||
|
profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString()
|
||||||
|
profileFunction.getProfile()?.let { currentProfile ->
|
||||||
|
profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?:currentProfile, LocalInsulin(""), injector)
|
||||||
|
}
|
||||||
|
autotunePlugin.selectedProfile = profileName
|
||||||
|
resetParam()
|
||||||
|
}
|
||||||
|
updateGui()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.autotuneCopylocal.setOnClickListener {
|
||||||
|
val localName = rh.gs(R.string.autotune_tunedprofile_name) + " " + dateUtil.dateAndTimeString(autotunePlugin.lastRun)
|
||||||
|
val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false)
|
||||||
|
autotunePlugin.tunedProfile?.let { tunedProfile ->
|
||||||
|
showConfirmation(requireContext(),
|
||||||
|
rh.gs(R.string.autotune_copy_localprofile_button),
|
||||||
|
rh.gs(R.string.autotune_copy_local_profile_message) + "\n" + localName + " " + dateUtil.dateAndTimeString(autotunePlugin.lastRun),
|
||||||
|
Runnable {
|
||||||
|
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(tunedProfile.getProfile(circadian), localName))
|
||||||
|
rxBus.send(EventLocalProfileChanged())
|
||||||
|
uel.log(
|
||||||
|
UserEntry.Action.NEW_PROFILE,
|
||||||
|
UserEntry.Sources.Autotune,
|
||||||
|
ValueWithUnit.SimpleString(localName)
|
||||||
|
)
|
||||||
|
updateGui()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.autotuneUpdateProfile.setOnClickListener {
|
||||||
|
val localName = autotunePlugin.pumpProfile.profilename
|
||||||
|
showConfirmation(requireContext(),
|
||||||
|
rh.gs(R.string.autotune_update_input_profile_button),
|
||||||
|
rh.gs(R.string.autotune_update_local_profile_message, localName),
|
||||||
|
Runnable {
|
||||||
|
autotunePlugin.tunedProfile?.profilename = localName
|
||||||
|
autotunePlugin.updateProfile(autotunePlugin.tunedProfile)
|
||||||
|
autotunePlugin.updateButtonVisibility = View.GONE
|
||||||
|
uel.log(
|
||||||
|
UserEntry.Action.STORE_PROFILE,
|
||||||
|
UserEntry.Sources.Autotune,
|
||||||
|
ValueWithUnit.SimpleString(localName)
|
||||||
|
)
|
||||||
|
updateGui()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.autotuneRevertProfile.setOnClickListener {
|
||||||
|
val localName = autotunePlugin.pumpProfile.profilename
|
||||||
|
showConfirmation(requireContext(),
|
||||||
|
rh.gs(R.string.autotune_revert_input_profile_button),
|
||||||
|
rh.gs(R.string.autotune_revert_local_profile_message, localName),
|
||||||
|
Runnable {
|
||||||
|
autotunePlugin.tunedProfile?.profilename = ""
|
||||||
|
autotunePlugin.updateProfile(autotunePlugin.pumpProfile)
|
||||||
|
autotunePlugin.updateButtonVisibility = View.VISIBLE
|
||||||
|
uel.log(
|
||||||
|
UserEntry.Action.STORE_PROFILE,
|
||||||
|
UserEntry.Sources.Autotune,
|
||||||
|
ValueWithUnit.SimpleString(localName)
|
||||||
|
)
|
||||||
|
updateGui()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.autotuneCheckInputProfile.setOnClickListener {
|
||||||
|
val pumpProfile = profileFunction.getProfile()?.let { currentProfile ->
|
||||||
|
profileStore.getSpecificProfile(profileName)?.let { specificProfile ->
|
||||||
|
ATProfile(ProfileSealed.Pure(specificProfile), LocalInsulin(""), injector).also {
|
||||||
|
it.profilename = profileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?: ATProfile(currentProfile, LocalInsulin(""), injector).also {
|
||||||
|
it.profilename = profileFunction.getProfileName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pumpProfile?.let {
|
||||||
|
ProfileViewerDialog().also { pvd ->
|
||||||
|
pvd.arguments = Bundle().also {
|
||||||
|
it.putLong("time", dateUtil.now())
|
||||||
|
it.putInt("mode", ProfileViewerDialog.Mode.CUSTOM_PROFILE.ordinal)
|
||||||
|
it.putString("customProfile", pumpProfile.profile.toPureNsJson(dateUtil).toString())
|
||||||
|
it.putString("customProfileUnits", profileFunction.getUnits().asText)
|
||||||
|
it.putString("customProfileName", pumpProfile.profilename)
|
||||||
|
}
|
||||||
|
}.show(childFragmentManager, "ProfileViewDialog")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.autotuneCompare.setOnClickListener {
|
||||||
|
val pumpProfile = autotunePlugin.pumpProfile
|
||||||
|
val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false)
|
||||||
|
val tunedprofile = if (circadian) autotunePlugin.tunedProfile?.circadianProfile else autotunePlugin.tunedProfile?.profile
|
||||||
|
ProfileViewerDialog().also { pvd ->
|
||||||
|
pvd.arguments = Bundle().also {
|
||||||
|
it.putLong("time", dateUtil.now())
|
||||||
|
it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
|
||||||
|
it.putString("customProfile", pumpProfile.profile.toPureNsJson(dateUtil).toString())
|
||||||
|
it.putString("customProfile2", tunedprofile?.toPureNsJson(dateUtil).toString())
|
||||||
|
it.putString("customProfileUnits", profileFunction.getUnits().asText)
|
||||||
|
it.putString("customProfileName", pumpProfile.profilename + "\n" + rh.gs(R.string.autotune_tunedprofile_name))
|
||||||
|
}
|
||||||
|
}.show(childFragmentManager, "ProfileViewDialog")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.autotuneProfileswitch.setOnClickListener {
|
||||||
|
val tunedProfile = autotunePlugin.tunedProfile
|
||||||
|
autotunePlugin.updateProfile(tunedProfile)
|
||||||
|
val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false)
|
||||||
|
tunedProfile?.let { tunedP ->
|
||||||
|
tunedP.profileStore(circadian)?.let {
|
||||||
|
showConfirmation(requireContext(),
|
||||||
|
rh.gs(R.string.activate_profile) + ": " + tunedP.profilename + " ?",
|
||||||
|
Runnable {
|
||||||
|
uel.log(
|
||||||
|
UserEntry.Action.STORE_PROFILE,
|
||||||
|
UserEntry.Sources.Autotune,
|
||||||
|
ValueWithUnit.SimpleString(tunedP.profilename)
|
||||||
|
)
|
||||||
|
val now = dateUtil.now()
|
||||||
|
if (profileFunction.createProfileSwitch(
|
||||||
|
it,
|
||||||
|
profileName = tunedP.profilename,
|
||||||
|
durationInMinutes = 0,
|
||||||
|
percentage = 100,
|
||||||
|
timeShiftInHours = 0,
|
||||||
|
timestamp = now
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
uel.log(
|
||||||
|
UserEntry.Action.PROFILE_SWITCH,
|
||||||
|
UserEntry.Sources.Autotune,
|
||||||
|
"Autotune AutoSwitch",
|
||||||
|
ValueWithUnit.SimpleString(autotunePlugin.tunedProfile!!.profilename)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rxBus.send(EventLocalProfileChanged())
|
||||||
|
updateGui()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventAutotuneUpdateGui::class.java)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
updateGui()
|
||||||
|
}, { fabricPrivacy.logException(it) })
|
||||||
|
checkNewDay()
|
||||||
|
binding.tuneDays.value = autotunePlugin.lastNbDays.toDouble()
|
||||||
|
updateGui()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
disposable.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun updateGui() {
|
||||||
|
_binding ?: return
|
||||||
|
profileStore = activePlugin.activeProfileSource.profile ?: ProfileStore(injector, JSONObject(), dateUtil)
|
||||||
|
profileName = if (binding.profileList.text.toString() == rh.gs(R.string.active)) "" else binding.profileList.text.toString()
|
||||||
|
profileFunction.getProfile()?.let { currentProfile ->
|
||||||
|
profile = ATProfile(profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } ?:currentProfile, LocalInsulin(""), injector)
|
||||||
|
}
|
||||||
|
val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
|
||||||
|
profileList.add(0, rh.gs(R.string.active))
|
||||||
|
context?.let { context ->
|
||||||
|
binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList))
|
||||||
|
} ?: return
|
||||||
|
// set selected to actual profile
|
||||||
|
if (autotunePlugin.selectedProfile.isNotEmpty())
|
||||||
|
binding.profileList.setText(autotunePlugin.selectedProfile, false)
|
||||||
|
else {
|
||||||
|
binding.profileList.setText(profileList[0], false)
|
||||||
|
}
|
||||||
|
binding.autotuneRun.visibility = View.GONE
|
||||||
|
binding.autotuneCheckInputProfile.visibility = View.GONE
|
||||||
|
binding.autotuneCopylocal.visibility = View.GONE
|
||||||
|
binding.autotuneUpdateProfile.visibility = View.GONE
|
||||||
|
binding.autotuneRevertProfile.visibility = View.GONE
|
||||||
|
binding.autotuneProfileswitch.visibility = View.GONE
|
||||||
|
binding.autotuneCompare.visibility = View.GONE
|
||||||
|
when {
|
||||||
|
autotunePlugin.calculationRunning -> {
|
||||||
|
binding.tuneWarning.text = rh.gs(R.string.autotune_warning_during_run)
|
||||||
|
}
|
||||||
|
autotunePlugin.lastRunSuccess -> {
|
||||||
|
binding.autotuneCopylocal.visibility = View.VISIBLE
|
||||||
|
binding.autotuneUpdateProfile.visibility = autotunePlugin.updateButtonVisibility
|
||||||
|
binding.autotuneRevertProfile.visibility = if (autotunePlugin.updateButtonVisibility == View.VISIBLE) View.GONE else View.VISIBLE
|
||||||
|
binding.autotuneProfileswitch.visibility = View.VISIBLE
|
||||||
|
binding.tuneWarning.text = rh.gs(R.string.autotune_warning_after_run)
|
||||||
|
binding.autotuneCompare.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
binding.autotuneRun.visibility = View.VISIBLE
|
||||||
|
binding.autotuneCheckInputProfile.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.tuneLastrun.text = dateUtil.dateAndTimeString(autotunePlugin.lastRun)
|
||||||
|
showResults()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkNewDay() {
|
||||||
|
val runToday = autotunePlugin.lastRun > MidnightTime.calc(dateUtil.now() - autotunePlugin.autotuneStartHour * 3600 * 1000L) + autotunePlugin.autotuneStartHour * 3600 * 1000L
|
||||||
|
if (runToday && autotunePlugin.result != "")
|
||||||
|
{
|
||||||
|
binding.tuneWarning.text = rh.gs(R.string.autotune_warning_after_run)
|
||||||
|
} else if (!runToday || autotunePlugin.result.isEmpty()) { //if new day reinit result, default days, warning and button's visibility
|
||||||
|
resetParam(!runToday)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addWarnings(): String {
|
||||||
|
var warning = ""
|
||||||
|
var nl = ""
|
||||||
|
if (profileFunction.getProfile() == null) {
|
||||||
|
warning = rh.gs(R.string.profileswitch_ismissing)
|
||||||
|
return warning
|
||||||
|
}
|
||||||
|
profileFunction.getProfile()?.let {
|
||||||
|
if (!profile.isValid) return rh.gs(R.string.autotune_profile_invalid)
|
||||||
|
if (profile.icSize > 1) {
|
||||||
|
warning += nl + rh.gs(R.string.autotune_ic_warning, profile.icSize, profile.ic)
|
||||||
|
nl = "\n"
|
||||||
|
}
|
||||||
|
if (profile.isfSize > 1) {
|
||||||
|
warning += nl + rh.gs(R.string.autotune_isf_warning, profile.isfSize, Profile.fromMgdlToUnits(profile.isf, profileFunction.getUnits()), profileFunction.getUnits().asText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return warning
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetParam(resetDay: Boolean = true) {
|
||||||
|
binding.tuneWarning.text = addWarnings()
|
||||||
|
if (resetDay)
|
||||||
|
autotunePlugin.lastNbDays = sp.getInt(R.string.key_autotune_default_tune_days, 5).toString()
|
||||||
|
autotunePlugin.result = ""
|
||||||
|
binding.autotuneResults.removeAllViews()
|
||||||
|
autotunePlugin.tunedProfile = null
|
||||||
|
autotunePlugin.lastRunSuccess = false
|
||||||
|
autotunePlugin.updateButtonVisibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textWatcher = object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable) { updateGui() }
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
if (binding.tuneDays.text.isNotEmpty()) {
|
||||||
|
try {
|
||||||
|
if (autotunePlugin.calculationRunning)
|
||||||
|
binding.tuneDays.value = autotunePlugin.lastNbDays.toDouble()
|
||||||
|
if (binding.tuneDays.value != autotunePlugin.lastNbDays.toDouble()) {
|
||||||
|
autotunePlugin.lastNbDays = binding.tuneDays.text
|
||||||
|
resetParam(false)
|
||||||
|
}
|
||||||
|
} catch (e:Exception) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showResults() {
|
||||||
|
Thread {
|
||||||
|
runOnUiThread {
|
||||||
|
binding.autotuneResults.removeAllViews()
|
||||||
|
if (autotunePlugin.result.isNotBlank()) {
|
||||||
|
var toMgDl = 1.0
|
||||||
|
if (profileFunction.getUnits() == GlucoseUnit.MMOL) toMgDl = Constants.MMOLL_TO_MGDL
|
||||||
|
var isf_Format = if (profileFunction.getUnits() == GlucoseUnit.MMOL) "%.2f" else "%.1f"
|
||||||
|
binding.autotuneResults.addView(
|
||||||
|
TableLayout(context).also { layout ->
|
||||||
|
layout.addView(
|
||||||
|
TextView(context).apply {
|
||||||
|
text = autotunePlugin.result
|
||||||
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
gravity = Gravity.CENTER_HORIZONTAL
|
||||||
|
setTextAppearance(android.R.style.TextAppearance_Material_Medium)
|
||||||
|
})
|
||||||
|
autotunePlugin.tunedProfile?.let { tuned ->
|
||||||
|
layout.addView(toTableRowHeader())
|
||||||
|
val tuneInsulin = sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)
|
||||||
|
if (tuneInsulin) {
|
||||||
|
layout.addView(toTableRowValue(rh.gs(R.string.insulin_peak), autotunePlugin.pumpProfile.localInsulin.peak.toDouble(), tuned.localInsulin.peak.toDouble(), "%.0f"))
|
||||||
|
layout.addView(toTableRowValue(rh.gs(R.string.dia), Round.roundTo(autotunePlugin.pumpProfile.localInsulin.dia, 0.1), Round.roundTo(tuned.localInsulin.dia, 0.1),"%.1f"))
|
||||||
|
}
|
||||||
|
layout.addView(toTableRowValue(rh.gs(R.string.isf_short), Round.roundTo(autotunePlugin.pumpProfile.isf / toMgDl, 0.001), Round.roundTo(tuned.isf / toMgDl, 0.001), isf_Format))
|
||||||
|
layout.addView(toTableRowValue(rh.gs(R.string.ic_short), Round.roundTo(autotunePlugin.pumpProfile.ic, 0.001), Round.roundTo(tuned.ic, 0.001), "%.2f"))
|
||||||
|
layout.addView(
|
||||||
|
TextView(context).apply {
|
||||||
|
text = rh.gs(R.string.basal)
|
||||||
|
setTypeface(typeface, Typeface.BOLD)
|
||||||
|
gravity = Gravity.CENTER_HORIZONTAL
|
||||||
|
setTextAppearance(android.R.style.TextAppearance_Material_Medium)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
layout.addView(toTableRowHeader(true))
|
||||||
|
var totalPump = 0.0
|
||||||
|
var totalTuned = 0.0
|
||||||
|
for (h in 0 until tuned.basal.size) {
|
||||||
|
val df = DecimalFormat("00")
|
||||||
|
val time = df.format(h.toLong()) + ":00"
|
||||||
|
totalPump += autotunePlugin.pumpProfile.basal[h]
|
||||||
|
totalTuned += tuned.basal[h]
|
||||||
|
layout.addView(toTableRowValue(time, autotunePlugin.pumpProfile.basal[h], tuned.basal[h], "%.3f", tuned.basalUntuned[h].toString()))
|
||||||
|
}
|
||||||
|
layout.addView(toTableRowValue("∑", totalPump, totalTuned, " "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
binding.autotuneResultsCard.visibility = if (autotunePlugin.calculationRunning && autotunePlugin.result.isEmpty()) View.GONE else View.VISIBLE
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toTableRowHeader(basal:Boolean = false): TableRow =
|
||||||
|
TableRow(context).also { header ->
|
||||||
|
val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f }
|
||||||
|
header.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT).apply { gravity = Gravity.CENTER_HORIZONTAL }
|
||||||
|
header.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 0 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = if (basal) rh.gs(R.string.time) else rh.gs(R.string.autotune_param)
|
||||||
|
})
|
||||||
|
header.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 1 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = rh.gs(R.string.profile)
|
||||||
|
})
|
||||||
|
header.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 2 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = rh.gs(R.string.autotune_tunedprofile_name)
|
||||||
|
})
|
||||||
|
header.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 3 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = rh.gs(R.string.autotune_percent)
|
||||||
|
})
|
||||||
|
header.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 4 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = if (basal) rh.gs(R.string.autotune_missing) else " "
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toTableRowValue(hour: String, inputValue: Double, tunedValue: Double, format:String = "%.3f", missing: String = ""): TableRow =
|
||||||
|
TableRow(context).also { row ->
|
||||||
|
val percentValue = Round.roundTo(tunedValue / inputValue * 100 - 100, 1.0).toInt().toString() + "%"
|
||||||
|
val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT).apply { weight = 1f }
|
||||||
|
row.layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT).apply { gravity = Gravity.CENTER_HORIZONTAL }
|
||||||
|
row.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 0 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = hour
|
||||||
|
})
|
||||||
|
row.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 1 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = String.format(format, inputValue)
|
||||||
|
})
|
||||||
|
row.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 2 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = String.format(format, tunedValue)
|
||||||
|
})
|
||||||
|
row.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 3 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = percentValue
|
||||||
|
})
|
||||||
|
row.addView(TextView(context).apply {
|
||||||
|
layoutParams = lp.apply { column = 4 }
|
||||||
|
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
|
||||||
|
text = missing
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log(message: String) {
|
||||||
|
autotuneFS.atLog("[Fragment] $message")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,386 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.Constants
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.*
|
||||||
|
import info.nightscout.androidaps.database.AppRepository
|
||||||
|
import info.nightscout.androidaps.database.embedments.InterfaceIDs
|
||||||
|
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||||
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
|
import info.nightscout.androidaps.database.entities.*
|
||||||
|
import info.nightscout.androidaps.extensions.durationInMinutes
|
||||||
|
import info.nightscout.androidaps.extensions.iobCalc
|
||||||
|
import info.nightscout.androidaps.extensions.toJson
|
||||||
|
import info.nightscout.androidaps.extensions.toTemporaryBasal
|
||||||
|
import info.nightscout.androidaps.interfaces.Profile
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.Round
|
||||||
|
import info.nightscout.androidaps.utils.T
|
||||||
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AutotuneIob @Inject constructor(
|
||||||
|
private val aapsLogger: AAPSLogger,
|
||||||
|
private val repository: AppRepository,
|
||||||
|
private val profileFunction: ProfileFunction,
|
||||||
|
private val sp: SP,
|
||||||
|
private val dateUtil: DateUtil,
|
||||||
|
private val activePlugin: ActivePlugin,
|
||||||
|
private val autotuneFS: AutotuneFS
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val nsTreatments = ArrayList<NsTreatment>()
|
||||||
|
private var dia: Double = Constants.defaultDIA
|
||||||
|
var boluses: MutableList<Bolus> = ArrayList()
|
||||||
|
var meals = ArrayList<Carbs>()
|
||||||
|
lateinit var glucose: List<GlucoseValue> // newest at index 0
|
||||||
|
private lateinit var tempBasals: MutableList<TemporaryBasal>
|
||||||
|
var startBG: Long = 0
|
||||||
|
var endBG: Long = 0
|
||||||
|
private fun range(): Long = (60 * 60 * 1000L * dia + T.hours(2).msecs()).toLong()
|
||||||
|
|
||||||
|
fun initializeData(from: Long, to: Long, tunedProfile: ATProfile) {
|
||||||
|
dia = tunedProfile.dia
|
||||||
|
startBG = from
|
||||||
|
endBG = to
|
||||||
|
nsTreatments.clear()
|
||||||
|
tempBasals = ArrayList<TemporaryBasal>()
|
||||||
|
initializeBgreadings(from, to)
|
||||||
|
initializeTreatmentData(from - range(), to)
|
||||||
|
initializeTempBasalData(from - range(), to, tunedProfile)
|
||||||
|
initializeExtendedBolusData(from - range(), to, tunedProfile)
|
||||||
|
Collections.sort(tempBasals) { o1: TemporaryBasal, o2: TemporaryBasal -> (o2.timestamp - o1.timestamp).toInt() }
|
||||||
|
// Without Neutral TBR, Autotune Web will ignore iob for periods without TBR running
|
||||||
|
addNeutralTempBasal(from - range(), to, tunedProfile)
|
||||||
|
Collections.sort(nsTreatments) { o1: NsTreatment, o2: NsTreatment -> (o2.date - o1.date).toInt() }
|
||||||
|
Collections.sort(boluses) { o1: Bolus, o2: Bolus -> (o2.timestamp - o1.timestamp).toInt() }
|
||||||
|
log.debug("D/AutotunePlugin: Nb Treatments: " + nsTreatments.size + " Nb meals: " + meals.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeBgreadings(from: Long, to: Long) {
|
||||||
|
glucose = repository.compatGetBgReadingsDataFromTime(from, to, false).blockingGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
//nsTreatment is used only for export data, meals is used in AutotunePrep
|
||||||
|
private fun initializeTreatmentData(from: Long, to: Long) {
|
||||||
|
val oldestBgDate = if (glucose.size > 0) glucose[glucose.size - 1].timestamp else from
|
||||||
|
log.debug("AutotunePlugin Check BG date: BG Size: " + glucose.size + " OldestBG: " + dateUtil.dateAndTimeAndSecondsString(oldestBgDate) + " to: " + dateUtil.dateAndTimeAndSecondsString(to))
|
||||||
|
val tmpCarbs = repository.getCarbsDataFromTimeToTimeExpanded(from, to, false).blockingGet()
|
||||||
|
log.debug("AutotunePlugin Nb treatments after query: " + tmpCarbs.size)
|
||||||
|
meals.clear()
|
||||||
|
boluses.clear()
|
||||||
|
var nbCarbs = 0
|
||||||
|
for (i in tmpCarbs.indices) {
|
||||||
|
val tp = tmpCarbs[i]
|
||||||
|
if (tp.isValid) {
|
||||||
|
nsTreatments.add(NsTreatment(tp))
|
||||||
|
//only carbs after first BGReadings are taken into account in calculation of Autotune
|
||||||
|
if (tp.amount > 0.0 && tp.timestamp >= oldestBgDate) meals.add(tmpCarbs[i])
|
||||||
|
if (tp.timestamp < to && tp.amount > 0.0)
|
||||||
|
nbCarbs++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val tmpBolus = repository.getBolusesDataFromTimeToTime(from, to, false).blockingGet()
|
||||||
|
var nbSMB = 0
|
||||||
|
var nbBolus = 0
|
||||||
|
for (i in tmpBolus.indices) {
|
||||||
|
val tp = tmpBolus[i]
|
||||||
|
if (tp.isValid && tp.type != Bolus.Type.PRIMING) {
|
||||||
|
boluses.add(tp)
|
||||||
|
nsTreatments.add(NsTreatment(tp))
|
||||||
|
//only carbs after first BGReadings are taken into account in calculation of Autotune
|
||||||
|
if (tp.timestamp < to) {
|
||||||
|
if (tp.type == Bolus.Type.SMB)
|
||||||
|
nbSMB++
|
||||||
|
else if (tp.amount > 0.0)
|
||||||
|
nbBolus++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//log.debug("AutotunePlugin Nb Meals: $nbCarbs Nb Bolus: $nbBolus Nb SMB: $nbSMB")
|
||||||
|
}
|
||||||
|
|
||||||
|
//nsTreatment is used only for export data
|
||||||
|
private fun initializeTempBasalData(from: Long, to: Long, tunedProfile: ATProfile) {
|
||||||
|
val tBRs = repository.getTemporaryBasalsDataFromTimeToTime(from, to, false).blockingGet()
|
||||||
|
//log.debug("D/AutotunePlugin tempBasal size before cleaning:" + tBRs.size);
|
||||||
|
for (i in tBRs.indices) {
|
||||||
|
if (tBRs[i].isValid)
|
||||||
|
toSplittedTimestampTB(tBRs[i], tunedProfile)
|
||||||
|
}
|
||||||
|
//log.debug("D/AutotunePlugin: tempBasal size: " + tempBasals.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nsTreatment is used only for export data
|
||||||
|
private fun initializeExtendedBolusData(from: Long, to: Long, tunedProfile: ATProfile) {
|
||||||
|
val extendedBoluses = repository.getExtendedBolusDataFromTimeToTime(from, to, false).blockingGet()
|
||||||
|
val pumpInterface = activePlugin.activePump
|
||||||
|
if (pumpInterface.isFakingTempsByExtendedBoluses) {
|
||||||
|
for (i in extendedBoluses.indices) {
|
||||||
|
val eb = extendedBoluses[i]
|
||||||
|
if (eb.isValid)
|
||||||
|
profileFunction.getProfile(eb.timestamp)?.let {
|
||||||
|
toSplittedTimestampTB(eb.toTemporaryBasal(it), tunedProfile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i in extendedBoluses.indices) {
|
||||||
|
val eb = extendedBoluses[i]
|
||||||
|
if (eb.isValid) {
|
||||||
|
nsTreatments.add(NsTreatment(eb))
|
||||||
|
boluses.addAll(convertToBoluses(eb))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addNeutralTempBasal will add a fake neutral TBR (100%) to have correct basal rate in exported file for periods without TBR running
|
||||||
|
// to be able to compare results between oref0 algo and aaps
|
||||||
|
private fun addNeutralTempBasal(from: Long, to: Long, tunedProfile: ATProfile) {
|
||||||
|
var previousStart = to
|
||||||
|
for (i in tempBasals.indices) {
|
||||||
|
val newStart = tempBasals[i].timestamp + tempBasals[i].duration
|
||||||
|
if (previousStart - newStart > T.mins(1).msecs()) { // fill neutral only if more than 1 min
|
||||||
|
val neutralTbr = TemporaryBasal(
|
||||||
|
isValid = true,
|
||||||
|
isAbsolute = false,
|
||||||
|
timestamp = newStart,
|
||||||
|
rate = 100.0,
|
||||||
|
duration = previousStart - newStart,
|
||||||
|
interfaceIDs_backing = InterfaceIDs(nightscoutId = "neutral_" + newStart.toString()),
|
||||||
|
type = TemporaryBasal.Type.NORMAL
|
||||||
|
)
|
||||||
|
toSplittedTimestampTB(neutralTbr, tunedProfile)
|
||||||
|
}
|
||||||
|
previousStart = tempBasals[i].timestamp
|
||||||
|
}
|
||||||
|
if (previousStart - from > T.mins(1).msecs()) { // fill neutral only if more than 1 min
|
||||||
|
val neutralTbr = TemporaryBasal(
|
||||||
|
isValid = true,
|
||||||
|
isAbsolute = false,
|
||||||
|
timestamp = from,
|
||||||
|
rate = 100.0,
|
||||||
|
duration = previousStart - from,
|
||||||
|
interfaceIDs_backing = InterfaceIDs(nightscoutId = "neutral_" + from.toString()),
|
||||||
|
type = TemporaryBasal.Type.NORMAL
|
||||||
|
)
|
||||||
|
toSplittedTimestampTB(neutralTbr, tunedProfile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toSplittedTimestampTB will split all TBR across hours in different TBR with correct absolute value to be sure to have correct basal rate
|
||||||
|
// even if profile rate is not the same
|
||||||
|
private fun toSplittedTimestampTB(tb: TemporaryBasal, tunedProfile: ATProfile) {
|
||||||
|
var splittedTimestamp = tb.timestamp
|
||||||
|
val cutInMilliSec = T.mins(30).msecs() //30 min to compare with oref0
|
||||||
|
var splittedDuration = tb.duration
|
||||||
|
if (tb.isValid && tb.durationInMinutes > 0) {
|
||||||
|
val endTimestamp = splittedTimestamp + splittedDuration
|
||||||
|
while (splittedDuration > 0) {
|
||||||
|
if (Profile.milliSecFromMidnight(splittedTimestamp) / cutInMilliSec == Profile.milliSecFromMidnight(endTimestamp) / cutInMilliSec) {
|
||||||
|
val newtb = TemporaryBasal(
|
||||||
|
isValid = true,
|
||||||
|
isAbsolute = tb.isAbsolute,
|
||||||
|
timestamp = splittedTimestamp,
|
||||||
|
rate = tb.rate,
|
||||||
|
duration = splittedDuration,
|
||||||
|
interfaceIDs_backing = tb.interfaceIDs_backing,
|
||||||
|
type = tb.type
|
||||||
|
)
|
||||||
|
tempBasals.add(newtb)
|
||||||
|
nsTreatments.add(NsTreatment(newtb))
|
||||||
|
splittedDuration = 0
|
||||||
|
val profile = profileFunction.getProfile(newtb.timestamp) ?:continue
|
||||||
|
boluses.addAll(convertToBoluses(newtb, profile, tunedProfile.profile)) //
|
||||||
|
// required for correct iob calculation with oref0 algo
|
||||||
|
} else {
|
||||||
|
val durationFilled = (cutInMilliSec - Profile.milliSecFromMidnight(splittedTimestamp) % cutInMilliSec)
|
||||||
|
val newtb = TemporaryBasal(
|
||||||
|
isValid = true,
|
||||||
|
isAbsolute = tb.isAbsolute,
|
||||||
|
timestamp = splittedTimestamp,
|
||||||
|
rate = tb.rate,
|
||||||
|
duration = durationFilled,
|
||||||
|
interfaceIDs_backing = tb.interfaceIDs_backing,
|
||||||
|
type = tb.type
|
||||||
|
)
|
||||||
|
tempBasals.add(newtb)
|
||||||
|
nsTreatments.add(NsTreatment(newtb))
|
||||||
|
splittedTimestamp += durationFilled
|
||||||
|
splittedDuration = splittedDuration - durationFilled
|
||||||
|
val profile = profileFunction.getProfile(newtb.timestamp) ?:continue
|
||||||
|
boluses.addAll(convertToBoluses(newtb, profile, tunedProfile.profile)) // required for correct iob calculation with oref0 algo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getIOB(time: Long, localInsulin: LocalInsulin): IobTotal {
|
||||||
|
val bolusIob = getCalculationToTimeTreatments(time, localInsulin).round()
|
||||||
|
return bolusIob
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCalculationToTimeTreatments(time: Long, localInsulin: LocalInsulin): IobTotal {
|
||||||
|
val total = IobTotal(time)
|
||||||
|
val detailedLog = sp.getBoolean(R.string.key_autotune_additional_log, false)
|
||||||
|
for (pos in boluses.indices) {
|
||||||
|
val t = boluses[pos]
|
||||||
|
if (!t.isValid) continue
|
||||||
|
if (t.timestamp > time || t.timestamp < time - localInsulin.duration) continue
|
||||||
|
val tIOB = t.iobCalc(time, localInsulin)
|
||||||
|
if (detailedLog)
|
||||||
|
log("iobCalc;${t.interfaceIDs.nightscoutId};$time;${t.timestamp};${tIOB.iobContrib};${tIOB.activityContrib};${dateUtil.dateAndTimeAndSecondsString(time)};${dateUtil.dateAndTimeAndSecondsString(t.timestamp)}")
|
||||||
|
total.iob += tIOB.iobContrib
|
||||||
|
total.activity += tIOB.activityContrib
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun convertToBoluses(eb: ExtendedBolus): MutableList<Bolus> {
|
||||||
|
val result: MutableList<Bolus> = ArrayList()
|
||||||
|
val tempBolusSize = 0.05
|
||||||
|
val tempBolusCount : Int = (eb.amount / tempBolusSize).roundToInt()
|
||||||
|
if(tempBolusCount > 0) {
|
||||||
|
val tempBolusSpacing = eb.duration / tempBolusCount
|
||||||
|
for (j in 0L until tempBolusCount) {
|
||||||
|
val calcDate = eb.timestamp + j * tempBolusSpacing
|
||||||
|
val bolusInterfaceIDs = InterfaceIDs().also { it.nightscoutId = eb.interfaceIDs.nightscoutId + "_eb_$j" }
|
||||||
|
val tempBolusPart = Bolus(
|
||||||
|
interfaceIDs_backing = bolusInterfaceIDs,
|
||||||
|
timestamp = calcDate,
|
||||||
|
amount = tempBolusSize,
|
||||||
|
type = Bolus.Type.NORMAL
|
||||||
|
)
|
||||||
|
result.add(tempBolusPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertToBoluses(tbr: TemporaryBasal, profile: Profile, tunedProfile: Profile): MutableList<Bolus> {
|
||||||
|
val result: MutableList<Bolus> = ArrayList()
|
||||||
|
val realDuration = tbr.durationInMinutes
|
||||||
|
val basalRate = profile.getBasal(tbr.timestamp)
|
||||||
|
val tunedRate = tunedProfile.getBasal(tbr.timestamp)
|
||||||
|
val netBasalRate = Round.roundTo(if (tbr.isAbsolute) {
|
||||||
|
tbr.rate - tunedRate
|
||||||
|
} else {
|
||||||
|
tbr.rate / 100.0 * basalRate - tunedRate
|
||||||
|
}, 0.001)
|
||||||
|
val tempBolusSize = if (netBasalRate < 0 ) -0.05 else 0.05
|
||||||
|
val netBasalAmount: Double = Round.roundTo(netBasalRate * realDuration / 60.0, 0.01)
|
||||||
|
val tempBolusCount : Int = (netBasalAmount / tempBolusSize).roundToInt()
|
||||||
|
if(tempBolusCount > 0) {
|
||||||
|
val tempBolusSpacing = realDuration * 60 * 1000 / tempBolusCount
|
||||||
|
for (j in 0L until tempBolusCount) {
|
||||||
|
val calcDate = tbr.timestamp + j * tempBolusSpacing
|
||||||
|
val bolusInterfaceIDs = InterfaceIDs().also { it.nightscoutId = tbr.interfaceIDs.nightscoutId + "_tbr_$j" }
|
||||||
|
val tempBolusPart = Bolus(
|
||||||
|
interfaceIDs_backing = bolusInterfaceIDs,
|
||||||
|
timestamp = calcDate,
|
||||||
|
amount = tempBolusSize,
|
||||||
|
type = Bolus.Type.NORMAL
|
||||||
|
)
|
||||||
|
result.add(tempBolusPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun glucoseToJSON(): String {
|
||||||
|
val glucoseJson = JSONArray()
|
||||||
|
for (bgreading in glucose)
|
||||||
|
glucoseJson.put(bgreading.toJson(true, dateUtil))
|
||||||
|
return glucoseJson.toString(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bolusesToJSON(): String {
|
||||||
|
val bolusesJson = JSONArray()
|
||||||
|
for (bolus in boluses)
|
||||||
|
bolusesJson.put(bolus.toJson(true, dateUtil))
|
||||||
|
return bolusesJson.toString(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nsHistoryToJSON(): String {
|
||||||
|
val json = JSONArray()
|
||||||
|
for (t in nsTreatments) {
|
||||||
|
json.put(t.toJson())
|
||||||
|
}
|
||||||
|
return json.toString(2).replace("\\/", "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
//I add this internal class to be able to export easily ns-treatment files with same contain and format than NS query used by oref0-autotune
|
||||||
|
private inner class NsTreatment {
|
||||||
|
|
||||||
|
var date: Long = 0
|
||||||
|
var eventType: TherapyEvent.Type? = null
|
||||||
|
var carbsTreatment: Carbs? = null
|
||||||
|
var bolusTreatment: Bolus? = null
|
||||||
|
var temporaryBasal: TemporaryBasal? = null
|
||||||
|
var extendedBolus: ExtendedBolus? = null
|
||||||
|
|
||||||
|
constructor(t: Carbs) {
|
||||||
|
carbsTreatment = t
|
||||||
|
date = t.timestamp
|
||||||
|
eventType = TherapyEvent.Type.CARBS_CORRECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(t: Bolus) {
|
||||||
|
bolusTreatment = t
|
||||||
|
date = t.timestamp
|
||||||
|
eventType = TherapyEvent.Type.CORRECTION_BOLUS
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(t: TemporaryBasal) {
|
||||||
|
temporaryBasal = t
|
||||||
|
date = t.timestamp
|
||||||
|
eventType = TherapyEvent.Type.TEMPORARY_BASAL
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(t: ExtendedBolus) {
|
||||||
|
extendedBolus = t
|
||||||
|
date = t.timestamp
|
||||||
|
eventType = TherapyEvent.Type.COMBO_BOLUS
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJson(): JSONObject? {
|
||||||
|
val cPjson = JSONObject()
|
||||||
|
return when (eventType) {
|
||||||
|
TherapyEvent.Type.TEMPORARY_BASAL ->
|
||||||
|
temporaryBasal?.let { tbr ->
|
||||||
|
val profile = profileFunction.getProfile(tbr.timestamp)
|
||||||
|
profile?.let {
|
||||||
|
tbr.toJson(true, it, dateUtil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TherapyEvent.Type.COMBO_BOLUS ->
|
||||||
|
extendedBolus?.let {
|
||||||
|
val profile = profileFunction.getProfile(it.timestamp)
|
||||||
|
it.toJson(true, profile!!, dateUtil)
|
||||||
|
}
|
||||||
|
TherapyEvent.Type.CORRECTION_BOLUS -> bolusTreatment?.toJson(true, dateUtil)
|
||||||
|
TherapyEvent.Type.CARBS_CORRECTION -> carbsTreatment?.toJson(true, dateUtil)
|
||||||
|
else -> cPjson
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log(message: String) {
|
||||||
|
autotuneFS.atLog("[iob] $message")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(AutotunePlugin::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,309 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.LocalInsulin
|
||||||
|
import info.nightscout.androidaps.data.ProfileSealed
|
||||||
|
import info.nightscout.androidaps.database.entities.UserEntry
|
||||||
|
import info.nightscout.androidaps.database.entities.ValueWithUnit
|
||||||
|
import info.nightscout.androidaps.interfaces.*
|
||||||
|
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui
|
||||||
|
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
|
||||||
|
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.MidnightTime
|
||||||
|
import info.nightscout.androidaps.utils.T
|
||||||
|
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||||
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adaptation from oref0 autotune started by philoul on 2020 (complete refactoring of AutotunePlugin initialised by Rumen Georgiev on 1/29/2018.)
|
||||||
|
*
|
||||||
|
* TODO: replace Thread by Worker
|
||||||
|
* TODO: future version: Allow day of the week selection to tune specifics days (training days, working days, WE days)
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AutotunePlugin @Inject constructor(
|
||||||
|
injector: HasAndroidInjector,
|
||||||
|
resourceHelper: ResourceHelper,
|
||||||
|
private val sp: SP,
|
||||||
|
private val rxBus: RxBus,
|
||||||
|
private val profileFunction: ProfileFunction,
|
||||||
|
private val dateUtil: DateUtil,
|
||||||
|
private val activePlugin: ActivePlugin,
|
||||||
|
private val localProfilePlugin: LocalProfilePlugin,
|
||||||
|
private val autotuneFS: AutotuneFS,
|
||||||
|
private val autotuneIob: AutotuneIob,
|
||||||
|
private val autotunePrep: AutotunePrep,
|
||||||
|
private val autotuneCore: AutotuneCore,
|
||||||
|
private val buildHelper:BuildHelper,
|
||||||
|
private val uel: UserEntryLogger,
|
||||||
|
aapsLogger: AAPSLogger
|
||||||
|
) : PluginBase(PluginDescription()
|
||||||
|
.mainType(PluginType.GENERAL)
|
||||||
|
.fragmentClass(AutotuneFragment::class.qualifiedName)
|
||||||
|
.pluginIcon(R.drawable.ic_autotune)
|
||||||
|
.pluginName(R.string.autotune)
|
||||||
|
.shortName(R.string.autotune_shortname)
|
||||||
|
.preferencesId(R.xml.pref_autotune)
|
||||||
|
.description(R.string.autotune_description),
|
||||||
|
aapsLogger, resourceHelper, injector
|
||||||
|
), Autotune {
|
||||||
|
@Volatile override var lastRunSuccess: Boolean = false
|
||||||
|
@Volatile var result: String = ""
|
||||||
|
@Volatile var calculationRunning: Boolean = false
|
||||||
|
@Volatile var lastRun: Long = 0
|
||||||
|
@Volatile var selectedProfile = ""
|
||||||
|
@Volatile var lastNbDays: String = ""
|
||||||
|
@Volatile var updateButtonVisibility: Int = 0
|
||||||
|
@Volatile lateinit var pumpProfile: ATProfile
|
||||||
|
@Volatile var tunedProfile: ATProfile? = null
|
||||||
|
private var preppedGlucose: PreppedGlucose? = null
|
||||||
|
private lateinit var profile: Profile
|
||||||
|
val autotuneStartHour: Int = 4
|
||||||
|
|
||||||
|
override fun aapsAutotune(daysBack: Int, autoSwitch: Boolean, profileToTune: String): String {
|
||||||
|
tunedProfile = null
|
||||||
|
updateButtonVisibility = View.GONE
|
||||||
|
lastRunSuccess = false
|
||||||
|
var logResult = ""
|
||||||
|
result = ""
|
||||||
|
if (profileFunction.getProfile() == null) {
|
||||||
|
result = rh.gs(R.string.profileswitch_ismissing)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
val detailedLog = sp.getBoolean(R.string.key_autotune_additional_log, false)
|
||||||
|
calculationRunning = true
|
||||||
|
lastNbDays = "" + daysBack
|
||||||
|
lastRun = dateUtil.now()
|
||||||
|
val profileStore = activePlugin.activeProfileSource.profile ?: return rh.gs(R.string.profileswitch_ismissing)
|
||||||
|
selectedProfile = if (profileToTune.isEmpty()) profileFunction.getProfileName() else profileToTune
|
||||||
|
profileFunction.getProfile()?.let { currentProfile ->
|
||||||
|
profile = profileStore.getSpecificProfile(profileToTune)?.let { ProfileSealed.Pure(it) } ?: currentProfile
|
||||||
|
}
|
||||||
|
val localInsulin = LocalInsulin("PumpInsulin", activePlugin.activeInsulin.peak, profile.dia) // var because localInsulin could be updated later with Tune Insulin peak/dia
|
||||||
|
|
||||||
|
log("Start Autotune with $daysBack days back")
|
||||||
|
autotuneFS.createAutotuneFolder() //create autotune subfolder for autotune files if not exists
|
||||||
|
autotuneFS.deleteAutotuneFiles() //clean autotune folder before run
|
||||||
|
// Today at 4 AM
|
||||||
|
var endTime = MidnightTime.calc(lastRun) + autotuneStartHour * 60 * 60 * 1000L
|
||||||
|
if (endTime > lastRun) endTime -= 24 * 60 * 60 * 1000L // Check if 4 AM is before now
|
||||||
|
val starttime = endTime - daysBack * 24 * 60 * 60 * 1000L
|
||||||
|
autotuneFS.exportSettings(settings(lastRun, daysBack, starttime, endTime))
|
||||||
|
tunedProfile = ATProfile(profile, localInsulin, injector).also {
|
||||||
|
it.profilename = rh.gs(R.string.autotune_tunedprofile_name)
|
||||||
|
}
|
||||||
|
pumpProfile = ATProfile(profile, localInsulin, injector).also {
|
||||||
|
it.profilename = selectedProfile
|
||||||
|
}
|
||||||
|
autotuneFS.exportPumpProfile(pumpProfile)
|
||||||
|
|
||||||
|
for (i in 0 until daysBack) {
|
||||||
|
val from = starttime + i * 24 * 60 * 60 * 1000L // get 24 hours BG values from 4 AM to 4 AM next day
|
||||||
|
val to = from + 24 * 60 * 60 * 1000L
|
||||||
|
log("Tune day " + (i + 1) + " of " + daysBack)
|
||||||
|
tunedProfile?.let { tunedProfile ->
|
||||||
|
autotuneIob.initializeData(from, to, tunedProfile) //autotuneIob contains BG and Treatments data from history (<=> query for ns-treatments and ns-entries)
|
||||||
|
autotuneFS.exportEntries(autotuneIob) //<=> ns-entries.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine
|
||||||
|
autotuneFS.exportTreatments(autotuneIob) //<=> ns-treatments.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine (include treatments ,tempBasal and extended
|
||||||
|
preppedGlucose = autotunePrep.categorize(tunedProfile) //<=> autotune.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preppedGlucose == null || tunedProfile == null) {
|
||||||
|
result = rh.gs(R.string.autotune_error)
|
||||||
|
log(result)
|
||||||
|
calculationRunning = false
|
||||||
|
rxBus.send(EventAutotuneUpdateGui())
|
||||||
|
tunedProfile = null
|
||||||
|
autotuneFS.exportResult(result)
|
||||||
|
autotuneFS.exportLogAndZip(lastRun)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
preppedGlucose?.let { preppedGlucose -> //preppedGlucose and tunedProfile should never be null here
|
||||||
|
autotuneFS.exportPreppedGlucose(preppedGlucose)
|
||||||
|
tunedProfile = autotuneCore.tuneAllTheThings(preppedGlucose, tunedProfile!!, pumpProfile)
|
||||||
|
}
|
||||||
|
// localInsulin = LocalInsulin("TunedInsulin", tunedProfile!!.peak, tunedProfile!!.dia) // Todo: Add tune Insulin option
|
||||||
|
autotuneFS.exportTunedProfile(tunedProfile!!) //<=> newprofile.yyyymmdd.json files exported for results compare with oref0 autotune on virtual machine
|
||||||
|
if (i < daysBack - 1) {
|
||||||
|
log("Partial result for day ${i + 1}".trimIndent())
|
||||||
|
result = rh.gs(R.string.autotune_partial_result, i + 1, daysBack)
|
||||||
|
rxBus.send(EventAutotuneUpdateGui())
|
||||||
|
}
|
||||||
|
logResult = showResults(tunedProfile, pumpProfile)
|
||||||
|
if (detailedLog)
|
||||||
|
autotuneFS.exportLog(lastRun, i + 1)
|
||||||
|
}
|
||||||
|
result = rh.gs(R.string.autotune_result, dateUtil.dateAndTimeString(lastRun))
|
||||||
|
if (!detailedLog)
|
||||||
|
autotuneFS.exportLog(lastRun)
|
||||||
|
autotuneFS.exportResult(logResult)
|
||||||
|
autotuneFS.zipAutotune(lastRun)
|
||||||
|
updateButtonVisibility = View.VISIBLE
|
||||||
|
|
||||||
|
if (autoSwitch) {
|
||||||
|
val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false)
|
||||||
|
tunedProfile?.let { tunedP ->
|
||||||
|
tunedP.profilename = pumpProfile.profilename
|
||||||
|
updateProfile(tunedP)
|
||||||
|
uel.log(
|
||||||
|
UserEntry.Action.STORE_PROFILE,
|
||||||
|
UserEntry.Sources.Autotune,
|
||||||
|
ValueWithUnit.SimpleString(tunedP.profilename)
|
||||||
|
)
|
||||||
|
updateButtonVisibility = View.GONE
|
||||||
|
tunedP.profileStore(circadian)?.let { profilestore ->
|
||||||
|
if (profileFunction.createProfileSwitch(
|
||||||
|
profilestore,
|
||||||
|
profileName = tunedP.profilename,
|
||||||
|
durationInMinutes = 0,
|
||||||
|
percentage = 100,
|
||||||
|
timeShiftInHours = 0,
|
||||||
|
timestamp = dateUtil.now()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
log("Profile Switch succeed ${tunedP.profilename}")
|
||||||
|
uel.log(
|
||||||
|
UserEntry.Action.PROFILE_SWITCH,
|
||||||
|
UserEntry.Sources.Autotune,
|
||||||
|
"Autotune AutoSwitch",
|
||||||
|
ValueWithUnit.SimpleString(tunedP.profilename))
|
||||||
|
}
|
||||||
|
rxBus.send(EventLocalProfileChanged())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastRunSuccess = true
|
||||||
|
sp.putLong(R.string.key_autotune_last_run, lastRun)
|
||||||
|
rxBus.send(EventAutotuneUpdateGui())
|
||||||
|
calculationRunning = false
|
||||||
|
tunedProfile?.let {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return "No Result" // should never occurs
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showResults(tunedProfile: ATProfile?, pumpProfile: ATProfile): String {
|
||||||
|
if (tunedProfile == null)
|
||||||
|
return "No Result" // should never occurs
|
||||||
|
val line = rh.gs(R.string.autotune_log_separator)
|
||||||
|
var strResult = line
|
||||||
|
strResult += rh.gs(R.string.autotune_log_title)
|
||||||
|
strResult += line
|
||||||
|
val tuneInsulin = sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)
|
||||||
|
if (tuneInsulin) {
|
||||||
|
strResult += rh.gs(R.string.autotune_log_peak, rh.gs(R.string.insulin_peak), pumpProfile.localInsulin.peak, tunedProfile.localInsulin.peak)
|
||||||
|
strResult += rh.gs(R.string.autotune_log_dia, rh.gs(R.string.ic_short), pumpProfile.localInsulin.dia, tunedProfile.localInsulin.dia)
|
||||||
|
}
|
||||||
|
// show ISF and CR
|
||||||
|
strResult += rh.gs(R.string.autotune_log_ic_isf, rh.gs(R.string.isf_short), pumpProfile.isf, tunedProfile.isf)
|
||||||
|
strResult += rh.gs(R.string.autotune_log_ic_isf, rh.gs(R.string.ic_short), pumpProfile.ic, tunedProfile.ic)
|
||||||
|
strResult += line
|
||||||
|
var totalBasal = 0.0
|
||||||
|
var totalTuned = 0.0
|
||||||
|
for (i in 0..23) {
|
||||||
|
totalBasal += pumpProfile.basal[i]
|
||||||
|
totalTuned += tunedProfile.basal[i]
|
||||||
|
val percentageChangeValue = tunedProfile.basal[i] / pumpProfile.basal[i] * 100 - 100
|
||||||
|
strResult += rh.gs(R.string.autotune_log_basal, i.toDouble(), pumpProfile.basal[i], tunedProfile.basal[i], tunedProfile.basalUntuned[i], percentageChangeValue)
|
||||||
|
}
|
||||||
|
strResult += line
|
||||||
|
strResult += rh.gs(R.string.autotune_log_sum_basal, totalBasal, totalTuned)
|
||||||
|
strResult += line
|
||||||
|
log(strResult)
|
||||||
|
return strResult
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun settings(runDate: Long, nbDays: Int, firstloopstart: Long, lastloopend: Long): String {
|
||||||
|
var jsonString = ""
|
||||||
|
val jsonSettings = JSONObject()
|
||||||
|
val insulinInterface = activePlugin.activeInsulin
|
||||||
|
val utcOffset = T.msecs(TimeZone.getDefault().getOffset(dateUtil.now()).toLong()).hours()
|
||||||
|
val startDateString = dateUtil.toISOString(firstloopstart).substring(0,10)
|
||||||
|
val endDateString = dateUtil.toISOString(lastloopend - 24 * 60 * 60 * 1000L).substring(0,10)
|
||||||
|
val nsUrl = sp.getString(R.string.key_nsclientinternal_url, "")
|
||||||
|
val optCategorizeUam = if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) "-c=true" else ""
|
||||||
|
val optInsulinCurve = if (sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)) "-i=true" else ""
|
||||||
|
try {
|
||||||
|
jsonSettings.put("datestring", dateUtil.toISOString(runDate))
|
||||||
|
jsonSettings.put("dateutc", dateUtil.toISOAsUTC(runDate))
|
||||||
|
jsonSettings.put("utcOffset", utcOffset)
|
||||||
|
jsonSettings.put("units", profileFunction.getUnits().asText)
|
||||||
|
jsonSettings.put("timezone", TimeZone.getDefault().id)
|
||||||
|
jsonSettings.put("url_nightscout", sp.getString(R.string.key_nsclientinternal_url, ""))
|
||||||
|
jsonSettings.put("nbdays", nbDays)
|
||||||
|
jsonSettings.put("startdate", startDateString)
|
||||||
|
jsonSettings.put("enddate", endDateString)
|
||||||
|
// command to change timezone
|
||||||
|
jsonSettings.put("timezone_command", "sudo ln -sf /usr/share/zoneinfo/" + TimeZone.getDefault().id + " /etc/localtime")
|
||||||
|
// oref0_command is for running oref0-autotune on a virtual machine in a dedicated ~/aaps subfolder
|
||||||
|
jsonSettings.put("oref0_command", "oref0-autotune -d=~/aaps -n=$nsUrl -s=$startDateString -e=$endDateString $optCategorizeUam $optInsulinCurve")
|
||||||
|
// aaps_command is for running modified oref0-autotune with exported data from aaps (ns-entries and ns-treatment json files copied in ~/aaps/autotune folder and pumpprofile.json copied in ~/aaps/settings/
|
||||||
|
jsonSettings.put("aaps_command", "aaps-autotune -d=~/aaps -s=$startDateString -e=$endDateString $optCategorizeUam $optInsulinCurve")
|
||||||
|
jsonSettings.put("categorize_uam_as_basal", sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false))
|
||||||
|
jsonSettings.put("tune_insulin_curve", false)
|
||||||
|
|
||||||
|
val peaktime: Int = insulinInterface.peak
|
||||||
|
if (insulinInterface.id === Insulin.InsulinType.OREF_ULTRA_RAPID_ACTING)
|
||||||
|
jsonSettings.put("curve","ultra-rapid")
|
||||||
|
else if (insulinInterface.id === Insulin.InsulinType.OREF_RAPID_ACTING)
|
||||||
|
jsonSettings.put("curve", "rapid-acting")
|
||||||
|
else if (insulinInterface.id === Insulin.InsulinType.OREF_LYUMJEV) {
|
||||||
|
jsonSettings.put("curve", "ultra-rapid")
|
||||||
|
jsonSettings.put("useCustomPeakTime", true)
|
||||||
|
jsonSettings.put("insulinPeakTime", peaktime)
|
||||||
|
} else if (insulinInterface.id === Insulin.InsulinType.OREF_FREE_PEAK) {
|
||||||
|
jsonSettings.put("curve", if (peaktime > 55) "rapid-acting" else "ultra-rapid")
|
||||||
|
jsonSettings.put("useCustomPeakTime", true)
|
||||||
|
jsonSettings.put("insulinPeakTime", peaktime)
|
||||||
|
}
|
||||||
|
jsonString = jsonSettings.toString(4).replace("\\/", "/")
|
||||||
|
} catch (e: JSONException) { }
|
||||||
|
return jsonString
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProfile(newProfile: ATProfile?) {
|
||||||
|
if (newProfile == null) return
|
||||||
|
val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false)
|
||||||
|
val profileStore = activePlugin.activeProfileSource.profile ?: ProfileStore(injector, JSONObject(), dateUtil)
|
||||||
|
val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
|
||||||
|
var indexLocalProfile = -1
|
||||||
|
for (p in profileList.indices)
|
||||||
|
if (profileList[p] == newProfile.profilename)
|
||||||
|
indexLocalProfile = p
|
||||||
|
if (indexLocalProfile == -1) {
|
||||||
|
localProfilePlugin.addProfile(localProfilePlugin.copyFrom(newProfile.getProfile(circadian), newProfile.profilename))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localProfilePlugin.currentProfileIndex = indexLocalProfile
|
||||||
|
localProfilePlugin.currentProfile()?.dia = newProfile.dia
|
||||||
|
localProfilePlugin.currentProfile()?.basal = newProfile.basal()
|
||||||
|
localProfilePlugin.currentProfile()?.ic = newProfile.ic(circadian)
|
||||||
|
localProfilePlugin.currentProfile()?.isf = newProfile.isf(circadian)
|
||||||
|
localProfilePlugin.storeSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun log(message: String) {
|
||||||
|
atLog("[Plugin] $message")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun specialEnableCondition(): Boolean = buildHelper.isEngineeringMode() && buildHelper.isDev()
|
||||||
|
|
||||||
|
override fun atLog(message: String) {
|
||||||
|
autotuneFS.atLog(message)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,536 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.LocalInsulin
|
||||||
|
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||||
|
import info.nightscout.androidaps.plugins.general.autotune.data.*
|
||||||
|
import info.nightscout.androidaps.database.entities.Bolus
|
||||||
|
import info.nightscout.androidaps.database.entities.Carbs
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.MidnightTime
|
||||||
|
import info.nightscout.androidaps.utils.Round
|
||||||
|
import info.nightscout.androidaps.utils.T
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AutotunePrep @Inject constructor(
|
||||||
|
private val sp: SP,
|
||||||
|
private val dateUtil: DateUtil,
|
||||||
|
private val autotuneFS: AutotuneFS,
|
||||||
|
private val autotuneIob: AutotuneIob
|
||||||
|
) {
|
||||||
|
fun categorize(tunedprofile: ATProfile): PreppedGlucose? {
|
||||||
|
val preppedGlucose = categorizeBGDatums(tunedprofile, tunedprofile.localInsulin)
|
||||||
|
val tuneInsulin = sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)
|
||||||
|
if (tuneInsulin) {
|
||||||
|
var minDeviations = 1000000.0
|
||||||
|
val diaDeviations: MutableList<DiaDeviation> = ArrayList()
|
||||||
|
val peakDeviations: MutableList<PeakDeviation> = ArrayList()
|
||||||
|
val currentDIA = tunedprofile.localInsulin.dia
|
||||||
|
val currentPeak = tunedprofile.localInsulin.peak
|
||||||
|
|
||||||
|
var dia = currentDIA - 2
|
||||||
|
val endDIA = currentDIA + 2
|
||||||
|
while (dia <= endDIA)
|
||||||
|
{
|
||||||
|
var sqrtDeviations = 0.0
|
||||||
|
var deviations = 0.0
|
||||||
|
var deviationsSq = 0.0
|
||||||
|
val localInsulin = LocalInsulin("Ins_$currentPeak-$dia", currentPeak, dia)
|
||||||
|
val curve_output = categorizeBGDatums(tunedprofile, localInsulin, false)
|
||||||
|
val basalGlucose = curve_output?.basalGlucoseData
|
||||||
|
|
||||||
|
basalGlucose?.let {
|
||||||
|
for (hour in 0..23) {
|
||||||
|
for (i in 0..(basalGlucose.size-1)) {
|
||||||
|
val myHour = ((basalGlucose[i].date - MidnightTime.calc(basalGlucose[i].date)) / T.hours(1).msecs()).toInt()
|
||||||
|
if (hour == myHour) {
|
||||||
|
sqrtDeviations += Math.pow(Math.abs(basalGlucose[i].deviation), 0.5)
|
||||||
|
deviations += Math.abs(basalGlucose[i].deviation)
|
||||||
|
deviationsSq += Math.pow(basalGlucose[i].deviation, 2.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val meanDeviation = Round.roundTo(Math.abs(deviations / basalGlucose.size), 0.001)
|
||||||
|
val smrDeviation = Round.roundTo(Math.pow(sqrtDeviations / basalGlucose.size, 2.0), 0.001)
|
||||||
|
val rmsDeviation = Round.roundTo(Math.pow(deviationsSq / basalGlucose.size, 0.5), 0.001)
|
||||||
|
log("insulinEndTime $dia meanDeviation: $meanDeviation SMRDeviation: $smrDeviation RMSDeviation: $rmsDeviation (mg/dL)")
|
||||||
|
diaDeviations.add(
|
||||||
|
DiaDeviation(
|
||||||
|
dia = dia,
|
||||||
|
meanDeviation = meanDeviation,
|
||||||
|
smrDeviation = smrDeviation,
|
||||||
|
rmsDeviation = rmsDeviation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
preppedGlucose?.diaDeviations = diaDeviations
|
||||||
|
|
||||||
|
deviations = Round.roundTo(deviations, 0.001)
|
||||||
|
if (deviations < minDeviations)
|
||||||
|
minDeviations = Round.roundTo(deviations, 0.001)
|
||||||
|
dia += 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// consoleError('Optimum insulinEndTime', newDIA, 'mean deviation:', JSMath.Round(minDeviations/basalGlucose.length*1000)/1000, '(mg/dL)');
|
||||||
|
//consoleError(diaDeviations);
|
||||||
|
|
||||||
|
minDeviations = 1000000.0
|
||||||
|
var peak = currentPeak - 10
|
||||||
|
val endPeak = currentPeak + 10
|
||||||
|
while (peak <= endPeak)
|
||||||
|
{
|
||||||
|
var sqrtDeviations = 0.0
|
||||||
|
var deviations = 0.0
|
||||||
|
var deviationsSq = 0.0
|
||||||
|
val localInsulin = LocalInsulin("Ins_$peak-$currentDIA", peak, currentDIA)
|
||||||
|
val curve_output = categorizeBGDatums(tunedprofile, localInsulin, false)
|
||||||
|
val basalGlucose = curve_output?.basalGlucoseData
|
||||||
|
|
||||||
|
basalGlucose?.let {
|
||||||
|
for (hour in 0..23) {
|
||||||
|
for (i in 0..(basalGlucose.size - 1)) {
|
||||||
|
val myHour = ((basalGlucose[i].date - MidnightTime.calc(basalGlucose[i].date)) / T.hours(1).msecs()).toInt()
|
||||||
|
if (hour == myHour) {
|
||||||
|
//console.error(basalGlucose[i].deviation);
|
||||||
|
sqrtDeviations += Math.pow(Math.abs(basalGlucose[i].deviation), 0.5)
|
||||||
|
deviations += Math.abs(basalGlucose[i].deviation)
|
||||||
|
deviationsSq += Math.pow(basalGlucose[i].deviation, 2.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val meanDeviation = Round.roundTo(deviations / basalGlucose.size, 0.001)
|
||||||
|
val smrDeviation = Round.roundTo(Math.pow(sqrtDeviations / basalGlucose.size, 2.0), 0.001)
|
||||||
|
val rmsDeviation = Round.roundTo(Math.pow(deviationsSq / basalGlucose.size, 0.5), 0.001)
|
||||||
|
log("insulinPeakTime $peak meanDeviation: $meanDeviation SMRDeviation: $smrDeviation RMSDeviation: $rmsDeviation (mg/dL)")
|
||||||
|
peakDeviations.add(
|
||||||
|
PeakDeviation
|
||||||
|
(
|
||||||
|
peak = peak,
|
||||||
|
meanDeviation = meanDeviation,
|
||||||
|
smrDeviation = smrDeviation,
|
||||||
|
rmsDeviation = rmsDeviation,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
deviations = Round.roundTo(deviations, 0.001);
|
||||||
|
if (deviations < minDeviations)
|
||||||
|
minDeviations = Round.roundTo(deviations, 0.001)
|
||||||
|
peak += 5
|
||||||
|
}
|
||||||
|
//consoleError($"Optimum insulinPeakTime {newPeak} mean deviation: {JSMath.Round(minDeviations/basalGlucose.Count, 3)} (mg/dL)");
|
||||||
|
//consoleError(peakDeviations);
|
||||||
|
preppedGlucose?.peakDeviations = peakDeviations
|
||||||
|
}
|
||||||
|
|
||||||
|
return preppedGlucose
|
||||||
|
}
|
||||||
|
|
||||||
|
// private static Logger log = LoggerFactory.getLogger(AutotunePlugin.class);
|
||||||
|
fun categorizeBGDatums(tunedprofile: ATProfile, localInsulin: LocalInsulin, verbose: Boolean = true): PreppedGlucose? {
|
||||||
|
//lib/meals is called before to get only meals data (in AAPS it's done in AutotuneIob)
|
||||||
|
val treatments: MutableList<Carbs> = autotuneIob.meals
|
||||||
|
val boluses: MutableList<Bolus> = autotuneIob.boluses
|
||||||
|
// Bloc between #21 and # 54 replaced by bloc below (just remove BG value below 39, Collections.sort probably not necessary because BG values already sorted...)
|
||||||
|
val glucose = autotuneIob.glucose
|
||||||
|
val glucoseData: MutableList<GlucoseValue> = ArrayList()
|
||||||
|
for (i in glucose.indices) {
|
||||||
|
if (glucose[i].value > 39) {
|
||||||
|
glucoseData.add(glucose[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (glucose.size == 0 || glucoseData.size == 0 ) {
|
||||||
|
if (verbose)
|
||||||
|
log("No BG value received")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
glucoseData.sortWith(object: Comparator<GlucoseValue>{ override fun compare(o1: GlucoseValue, o2: GlucoseValue): Int = (o2.timestamp - o1.timestamp).toInt() })
|
||||||
|
|
||||||
|
// Bloc below replace bloc between #55 and #71
|
||||||
|
// boluses and maxCarbs not used here ?,
|
||||||
|
// IOBInputs are for iob calculation (done here in AutotuneIob Class)
|
||||||
|
//val boluses = 0
|
||||||
|
//val maxCarbs = 0
|
||||||
|
if (treatments.size == 0) {
|
||||||
|
if (verbose)
|
||||||
|
log("No Carbs entries")
|
||||||
|
//return null
|
||||||
|
}
|
||||||
|
if (autotuneIob.boluses.size == 0) {
|
||||||
|
if (verbose)
|
||||||
|
log("No treatment received")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
var csfGlucoseData: MutableList<BGDatum> = ArrayList()
|
||||||
|
var isfGlucoseData: MutableList<BGDatum> = ArrayList()
|
||||||
|
var basalGlucoseData: MutableList<BGDatum> = ArrayList()
|
||||||
|
val uamGlucoseData: MutableList<BGDatum> = ArrayList()
|
||||||
|
val crData: MutableList<CRDatum> = ArrayList()
|
||||||
|
|
||||||
|
//Bloc below replace bloc between #72 and #93
|
||||||
|
// I keep it because BG lines in log are consistent between AAPS and Oref0
|
||||||
|
val bucketedData: MutableList<BGDatum> = ArrayList()
|
||||||
|
bucketedData.add(BGDatum(glucoseData[0], dateUtil))
|
||||||
|
//int j=0;
|
||||||
|
var k = 0 // index of first value used by bucket
|
||||||
|
//for loop to validate and bucket the data
|
||||||
|
for (i in 1 until glucoseData.size) {
|
||||||
|
val BGTime = glucoseData[i].timestamp
|
||||||
|
val lastBGTime = glucoseData[k].timestamp
|
||||||
|
val elapsedMinutes = (BGTime - lastBGTime) / (60 * 1000)
|
||||||
|
if (Math.abs(elapsedMinutes) >= 2) {
|
||||||
|
//j++; // move to next bucket
|
||||||
|
k = i // store index of first value used by bucket
|
||||||
|
bucketedData.add(BGDatum(glucoseData[i], dateUtil))
|
||||||
|
} else {
|
||||||
|
// average all readings within time deadband
|
||||||
|
val average = glucoseData[k]
|
||||||
|
for (l in k + 1 until i + 1) {
|
||||||
|
average.value += glucoseData[l].value
|
||||||
|
}
|
||||||
|
average.value = average.value / (i - k + 1)
|
||||||
|
bucketedData.add(BGDatum(average, dateUtil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here treatments contains only meals data
|
||||||
|
// bloc between #94 and #114 remove meals before first BG value
|
||||||
|
|
||||||
|
// Bloc below replace bloc between #115 and #122 (initialize data before main loop)
|
||||||
|
// crInitialxx are declaration to be able to use these data in whole loop
|
||||||
|
var calculatingCR = false
|
||||||
|
var absorbing = false
|
||||||
|
var uam = false // unannounced meal
|
||||||
|
var mealCOB = 0.0
|
||||||
|
var mealCarbs = 0.0
|
||||||
|
var crCarbs = 0.0
|
||||||
|
var type = ""
|
||||||
|
var crInitialIOB = 0.0
|
||||||
|
var crInitialBG = 0.0
|
||||||
|
var crInitialCarbTime = 0L
|
||||||
|
|
||||||
|
//categorize.js#123 (Note: don't need fullHistory because data are managed in AutotuneIob Class)
|
||||||
|
//Here is main loop between #125 and #366
|
||||||
|
// main for loop
|
||||||
|
for (i in bucketedData.size - 5 downTo 1) {
|
||||||
|
val glucoseDatum = bucketedData[i]
|
||||||
|
//log.debug(glucoseDatum);
|
||||||
|
val BGTime = glucoseDatum.date
|
||||||
|
|
||||||
|
// As we're processing each data point, go through the treatment.carbs and see if any of them are older than
|
||||||
|
// the current BG data point. If so, add those carbs to COB.
|
||||||
|
val treatment = if (treatments.size > 0) treatments[treatments.size - 1] else null
|
||||||
|
var myCarbs = 0.0
|
||||||
|
if (treatment != null) {
|
||||||
|
if (treatment.timestamp < BGTime) {
|
||||||
|
if (treatment.amount > 0.0) {
|
||||||
|
mealCOB += treatment.amount
|
||||||
|
mealCarbs += treatment.amount
|
||||||
|
myCarbs = treatment.amount
|
||||||
|
}
|
||||||
|
treatments.removeAt(treatments.size - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var bg = 0.0
|
||||||
|
var avgDelta = 0.0
|
||||||
|
|
||||||
|
// TODO: re-implement interpolation to avoid issues here with gaps
|
||||||
|
// calculate avgDelta as last 4 datapoints to better catch more rises after COB hits zero
|
||||||
|
if (bucketedData[i].value != 0.0 && bucketedData[i + 4].value != 0.0) {
|
||||||
|
//log.debug(bucketedData[i]);
|
||||||
|
bg = bucketedData[i].value
|
||||||
|
if (bg < 40 || bucketedData[i + 4].value < 40) {
|
||||||
|
//process.stderr.write("!");
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
avgDelta = (bg - bucketedData[i + 4].value) / 4
|
||||||
|
} else {
|
||||||
|
if (verbose)
|
||||||
|
log("Could not find glucose data")
|
||||||
|
}
|
||||||
|
avgDelta = Round.roundTo(avgDelta, 0.01)
|
||||||
|
glucoseDatum.avgDelta = avgDelta
|
||||||
|
|
||||||
|
//sens = ISF
|
||||||
|
val sens = tunedprofile.isf
|
||||||
|
|
||||||
|
// for IOB calculations, use the average of the last 4 hours' basals to help convergence;
|
||||||
|
// this helps since the basal this hour could be different from previous, especially if with autotune they start to diverge.
|
||||||
|
// use the pumpbasalprofile to properly calculate IOB during periods where no temp basal is set
|
||||||
|
/* Note Philoul currentPumpBasal never used in oref0 Autotune code
|
||||||
|
var currentPumpBasal = pumpprofile.profile.getBasal(BGTime)
|
||||||
|
currentPumpBasal += pumpprofile.profile.getBasal(BGTime - 1 * 60 * 60 * 1000)
|
||||||
|
currentPumpBasal += pumpprofile.profile.getBasal(BGTime - 2 * 60 * 60 * 1000)
|
||||||
|
currentPumpBasal += pumpprofile.profile.getBasal(BGTime - 3 * 60 * 60 * 1000)
|
||||||
|
currentPumpBasal = Round.roundTo(currentPumpBasal / 4, 0.001) //CurrentPumpBasal for iob calculation is average of 4 last pumpProfile Basal rate
|
||||||
|
*/
|
||||||
|
// this is the current autotuned basal, used for everything else besides IOB calculations
|
||||||
|
val currentBasal = tunedprofile.getBasal(BGTime)
|
||||||
|
|
||||||
|
// basalBGI is BGI of basal insulin activity.
|
||||||
|
val basalBGI = Round.roundTo(currentBasal * sens / 60 * 5, 0.01) // U/hr * mg/dL/U * 1 hr / 60 minutes * 5 = mg/dL/5m
|
||||||
|
//console.log(JSON.stringify(IOBInputs.profile));
|
||||||
|
// call iob since calculated elsewhere
|
||||||
|
//var iob = getIOB(IOBInputs)[0];
|
||||||
|
// in autotune iob is calculated with 6 hours of history data, tunedProfile and average pumpProfile basal rate...
|
||||||
|
//log("currentBasal: " + currentBasal + " BGTime: " + BGTime + " / " + dateUtil!!.timeStringWithSeconds(BGTime) + "******************************************************************************************")
|
||||||
|
val iob = autotuneIob.getIOB(BGTime, localInsulin) // add localInsulin to be independent to InsulinPlugin
|
||||||
|
|
||||||
|
// activity times ISF times 5 minutes is BGI
|
||||||
|
val BGI = Round.roundTo(-iob.activity * sens * 5, 0.01)
|
||||||
|
// datum = one glucose data point (being prepped to store in output)
|
||||||
|
glucoseDatum.bgi = BGI
|
||||||
|
// calculating deviation
|
||||||
|
var deviation = avgDelta - BGI
|
||||||
|
|
||||||
|
// set positive deviations to zero if BG is below 80
|
||||||
|
if (bg < 80 && deviation > 0) {
|
||||||
|
deviation = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// rounding and storing deviation
|
||||||
|
deviation = Round.roundTo(deviation, 0.01)
|
||||||
|
glucoseDatum.deviation = deviation
|
||||||
|
|
||||||
|
// Then, calculate carb absorption for that 5m interval using the deviation.
|
||||||
|
if (mealCOB > 0) {
|
||||||
|
val ci = Math.max(deviation, sp.getDouble("openapsama_min_5m_carbimpact", 3.0))
|
||||||
|
val absorbed = ci * tunedprofile.ic / sens
|
||||||
|
// Store the COB, and use it as the starting point for the next data point.
|
||||||
|
mealCOB = Math.max(0.0, mealCOB - absorbed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate carb ratio (CR) independently of CSF and ISF
|
||||||
|
// Use the time period from meal bolus/carbs until COB is zero and IOB is < currentBasal/2
|
||||||
|
// For now, if another meal IOB/COB stacks on top of it, consider them together
|
||||||
|
// Compare beginning and ending BGs, and calculate how much more/less insulin is needed to neutralize
|
||||||
|
// Use entered carbs vs. starting IOB + delivered insulin + needed-at-end insulin to directly calculate CR.
|
||||||
|
if (mealCOB > 0 || calculatingCR) {
|
||||||
|
// set initial values when we first see COB
|
||||||
|
crCarbs += myCarbs
|
||||||
|
if (calculatingCR == false) {
|
||||||
|
crInitialIOB = iob.iob
|
||||||
|
crInitialBG = glucoseDatum.value
|
||||||
|
crInitialCarbTime = glucoseDatum.date
|
||||||
|
if (verbose)
|
||||||
|
log("CRInitialIOB: " + crInitialIOB + " CRInitialBG: " + crInitialBG + " CRInitialCarbTime: " + dateUtil.toISOString(crInitialCarbTime))
|
||||||
|
}
|
||||||
|
// keep calculatingCR as long as we have COB or enough IOB
|
||||||
|
if (mealCOB > 0 && i > 1) {
|
||||||
|
calculatingCR = true
|
||||||
|
} else if (iob.iob > currentBasal / 2 && i > 1) {
|
||||||
|
calculatingCR = true
|
||||||
|
// when COB=0 and IOB drops low enough, record end values and be done calculatingCR
|
||||||
|
} else {
|
||||||
|
val crEndIOB = iob.iob
|
||||||
|
val crEndBG = glucoseDatum.value
|
||||||
|
val crEndTime = glucoseDatum.date
|
||||||
|
if (verbose)
|
||||||
|
log("CREndIOB: " + crEndIOB + " CREndBG: " + crEndBG + " CREndTime: " + dateUtil.toISOString(crEndTime))
|
||||||
|
val crDatum = CRDatum(dateUtil)
|
||||||
|
crDatum.crInitialBG = crInitialBG
|
||||||
|
crDatum.crInitialIOB = crInitialIOB
|
||||||
|
crDatum.crInitialCarbTime = crInitialCarbTime
|
||||||
|
crDatum.crEndBG = crEndBG
|
||||||
|
crDatum.crEndIOB = crEndIOB
|
||||||
|
crDatum.crEndTime = crEndTime
|
||||||
|
crDatum.crCarbs = crCarbs
|
||||||
|
//log.debug(CRDatum);
|
||||||
|
//String crDataString = "{\"CRInitialIOB\": " + CRInitialIOB + ", \"CRInitialBG\": " + CRInitialBG + ", \"CRInitialCarbTime\": " + CRInitialCarbTime + ", \"CREndIOB\": " + CREndIOB + ", \"CREndBG\": " + CREndBG + ", \"CREndTime\": " + CREndTime + ", \"CRCarbs\": " + CRCarbs + "}";
|
||||||
|
val CRElapsedMinutes = Math.round((crEndTime - crInitialCarbTime) / (1000 * 60).toFloat())
|
||||||
|
|
||||||
|
//log.debug(CREndTime - CRInitialCarbTime, CRElapsedMinutes);
|
||||||
|
if (CRElapsedMinutes < 60 || i == 1 && mealCOB > 0) {
|
||||||
|
if (verbose)
|
||||||
|
log("Ignoring $CRElapsedMinutes m CR period.")
|
||||||
|
} else {
|
||||||
|
crData.add(crDatum)
|
||||||
|
}
|
||||||
|
crCarbs = 0.0
|
||||||
|
calculatingCR = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If mealCOB is zero but all deviations since hitting COB=0 are positive, assign those data points to CSFGlucoseData
|
||||||
|
// Once deviations go negative for at least one data point after COB=0, we can use the rest of the data to tune ISF or basals
|
||||||
|
if (mealCOB > 0 || absorbing || mealCarbs > 0) {
|
||||||
|
// if meal IOB has decayed, then end absorption after this data point unless COB > 0
|
||||||
|
absorbing = if (iob.iob < currentBasal / 2) {
|
||||||
|
false
|
||||||
|
// otherwise, as long as deviations are positive, keep tracking carb deviations
|
||||||
|
} else if (deviation > 0) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
if (!absorbing && mealCOB == 0.0) {
|
||||||
|
mealCarbs = 0.0
|
||||||
|
}
|
||||||
|
// check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag
|
||||||
|
//log.debug(type);
|
||||||
|
if (type != "csf") {
|
||||||
|
glucoseDatum.mealAbsorption = "start"
|
||||||
|
if (verbose)
|
||||||
|
log(glucoseDatum.mealAbsorption + " carb absorption")
|
||||||
|
}
|
||||||
|
type = "csf"
|
||||||
|
glucoseDatum.mealCarbs = mealCarbs.toInt()
|
||||||
|
//if (i == 0) { glucoseDatum.mealAbsorption = "end"; }
|
||||||
|
csfGlucoseData.add(glucoseDatum)
|
||||||
|
} else {
|
||||||
|
// check previous "type" value, and if it was csf, set a mealAbsorption end flag
|
||||||
|
if (type == "csf") {
|
||||||
|
csfGlucoseData[csfGlucoseData.size - 1].mealAbsorption = "end"
|
||||||
|
if (verbose)
|
||||||
|
log(csfGlucoseData[csfGlucoseData.size - 1].mealAbsorption + " carb absorption")
|
||||||
|
}
|
||||||
|
if (iob.iob > 2 * currentBasal || deviation > 6 || uam) {
|
||||||
|
uam = if (deviation > 0) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
if (type != "uam") {
|
||||||
|
glucoseDatum.uamAbsorption = "start"
|
||||||
|
if (verbose)
|
||||||
|
log(glucoseDatum.uamAbsorption + " unannnounced meal absorption")
|
||||||
|
}
|
||||||
|
type = "uam"
|
||||||
|
uamGlucoseData.add(glucoseDatum)
|
||||||
|
} else {
|
||||||
|
if (type == "uam") {
|
||||||
|
if (verbose)
|
||||||
|
log("end unannounced meal absorption")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through the remaining time periods and divide them into periods where scheduled basal insulin activity dominates. This would be determined by calculating the BG impact of scheduled basal insulin
|
||||||
|
// (for example 1U/hr * 48 mg/dL/U ISF = 48 mg/dL/hr = 5 mg/dL/5m), and comparing that to BGI from bolus and net basal insulin activity.
|
||||||
|
// When BGI is positive (insulin activity is negative), we want to use that data to tune basals
|
||||||
|
// When BGI is smaller than about 1/4 of basalBGI, we want to use that data to tune basals
|
||||||
|
// When BGI is negative and more than about 1/4 of basalBGI, we can use that data to tune ISF,
|
||||||
|
// unless avgDelta is positive: then that's some sort of unexplained rise we don't want to use for ISF, so that means basals
|
||||||
|
if (basalBGI > -4 * BGI) {
|
||||||
|
type = "basal"
|
||||||
|
basalGlucoseData.add(glucoseDatum)
|
||||||
|
} else {
|
||||||
|
if (avgDelta > 0 && avgDelta > -2 * BGI) {
|
||||||
|
//type="unknown"
|
||||||
|
type = "basal"
|
||||||
|
basalGlucoseData.add(glucoseDatum)
|
||||||
|
} else {
|
||||||
|
type = "ISF"
|
||||||
|
isfGlucoseData.add(glucoseDatum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// debug line to print out all the things
|
||||||
|
if (verbose)
|
||||||
|
log((if (absorbing) 1 else 0).toString() + " mealCOB: " + Round.roundTo(mealCOB, 0.1) + " mealCarbs: " + Math.round(mealCarbs) + " basalBGI: " + Round.roundTo(basalBGI, 0.1) + " BGI: " + Round.roundTo(BGI, 0.1) + " IOB: " + iob.iob+ " Activity: " + iob.activity + " at " + dateUtil.timeStringWithSeconds(BGTime) + " dev: " + deviation + " avgDelta: " + avgDelta + " " + type)
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************
|
||||||
|
|
||||||
|
// categorize.js Lines 372-383
|
||||||
|
for (crDatum in crData) {
|
||||||
|
crDatum.crInsulin = dosed(crDatum.crInitialCarbTime, crDatum.crEndTime, boluses)
|
||||||
|
}
|
||||||
|
// categorize.js Lines 384-436
|
||||||
|
val CSFLength = csfGlucoseData.size
|
||||||
|
var ISFLength = isfGlucoseData.size
|
||||||
|
val UAMLength = uamGlucoseData.size
|
||||||
|
var basalLength = basalGlucoseData.size
|
||||||
|
if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) {
|
||||||
|
if (verbose)
|
||||||
|
log("Categorizing all UAM data as basal.")
|
||||||
|
basalGlucoseData.addAll(uamGlucoseData)
|
||||||
|
} else if (CSFLength > 12) {
|
||||||
|
if (verbose)
|
||||||
|
log("Found at least 1h of carb: assuming meals were announced, and categorizing UAM data as basal.")
|
||||||
|
basalGlucoseData.addAll(uamGlucoseData)
|
||||||
|
} else {
|
||||||
|
if (2 * basalLength < UAMLength) {
|
||||||
|
//log.debug(basalGlucoseData, UAMGlucoseData);
|
||||||
|
if (verbose) {
|
||||||
|
log("Warning: too many deviations categorized as UnAnnounced Meals")
|
||||||
|
log("Adding $UAMLength UAM deviations to $basalLength basal ones")
|
||||||
|
}
|
||||||
|
basalGlucoseData.addAll(uamGlucoseData)
|
||||||
|
//log.debug(basalGlucoseData);
|
||||||
|
// if too much data is excluded as UAM, add in the UAM deviations, but then discard the highest 50%
|
||||||
|
basalGlucoseData.sortWith(object: Comparator<BGDatum>{ override fun compare(o1: BGDatum, o2: BGDatum): Int = (100 * o1.deviation - 100 * o2.deviation).toInt() }) //deviation rouded to 0.01, so *100 to avoid crash during sort
|
||||||
|
val newBasalGlucose: MutableList<BGDatum> = ArrayList()
|
||||||
|
for (i in 0 until basalGlucoseData.size / 2) {
|
||||||
|
newBasalGlucose.add(basalGlucoseData[i])
|
||||||
|
}
|
||||||
|
//log.debug(newBasalGlucose);
|
||||||
|
basalGlucoseData = newBasalGlucose
|
||||||
|
if (verbose)
|
||||||
|
log("and selecting the lowest 50%, leaving " + basalGlucoseData.size + " basal+UAM ones")
|
||||||
|
}
|
||||||
|
if (2 * ISFLength < UAMLength) {
|
||||||
|
if (verbose)
|
||||||
|
log("Adding $UAMLength UAM deviations to $ISFLength ISF ones")
|
||||||
|
isfGlucoseData.addAll(uamGlucoseData)
|
||||||
|
// if too much data is excluded as UAM, add in the UAM deviations to ISF, but then discard the highest 50%
|
||||||
|
isfGlucoseData.sortWith(object: Comparator<BGDatum>{ override fun compare(o1: BGDatum, o2: BGDatum): Int = (100 * o1.deviation - 100 * o2.deviation).toInt() }) //deviation rouded to 0.01, so *100 to avoid crash during sort
|
||||||
|
val newISFGlucose: MutableList<BGDatum> = ArrayList()
|
||||||
|
for (i in 0 until isfGlucoseData.size / 2) {
|
||||||
|
newISFGlucose.add(isfGlucoseData[i])
|
||||||
|
}
|
||||||
|
//console.error(newISFGlucose);
|
||||||
|
isfGlucoseData = newISFGlucose
|
||||||
|
if (verbose)
|
||||||
|
log("and selecting the lowest 50%, leaving " + isfGlucoseData.size + " ISF+UAM ones")
|
||||||
|
//log.error(ISFGlucoseData.length, UAMLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
basalLength = basalGlucoseData.size
|
||||||
|
ISFLength = isfGlucoseData.size
|
||||||
|
if (4 * basalLength + ISFLength < CSFLength && ISFLength < 10) {
|
||||||
|
if (verbose) {
|
||||||
|
log("Warning: too many deviations categorized as meals")
|
||||||
|
//log.debug("Adding",CSFLength,"CSF deviations to",basalLength,"basal ones");
|
||||||
|
//var basalGlucoseData = basalGlucoseData.concat(CSFGlucoseData);
|
||||||
|
log("Adding $CSFLength CSF deviations to $ISFLength ISF ones")
|
||||||
|
}
|
||||||
|
isfGlucoseData.addAll(csfGlucoseData)
|
||||||
|
csfGlucoseData = ArrayList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// categorize.js Lines 437-444
|
||||||
|
if (verbose)
|
||||||
|
log("CRData: " + crData.size + " CSFGlucoseData: " + csfGlucoseData.size + " ISFGlucoseData: " + isfGlucoseData.size + " BasalGlucoseData: " + basalGlucoseData.size)
|
||||||
|
|
||||||
|
return PreppedGlucose(autotuneIob.startBG, crData, csfGlucoseData, isfGlucoseData, basalGlucoseData, dateUtil)
|
||||||
|
}
|
||||||
|
|
||||||
|
//dosed.js full
|
||||||
|
private fun dosed(start: Long, end: Long, treatments: List<Bolus>): Double {
|
||||||
|
var insulinDosed = 0.0
|
||||||
|
if (treatments.size == 0) {
|
||||||
|
log("No treatments to process.")
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
for (treatment in treatments) {
|
||||||
|
if (treatment.amount != 0.0 && treatment.timestamp > start && treatment.timestamp <= end) {
|
||||||
|
insulinDosed += treatment.amount
|
||||||
|
//log("CRDATA;${dateUtil.toISOString(start)};${dateUtil.toISOString(end)};${treatment.timestamp};${treatment.amount};$insulinDosed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//log("insulin dosed: " + insulinDosed);
|
||||||
|
return Round.roundTo(insulinDosed, 0.001)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log(message: String) {
|
||||||
|
autotuneFS.atLog("[Prep] $message")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune.data
|
||||||
|
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.androidaps.core.R
|
||||||
|
import info.nightscout.androidaps.data.LocalInsulin
|
||||||
|
import info.nightscout.androidaps.data.ProfileSealed
|
||||||
|
import info.nightscout.androidaps.data.PureProfile
|
||||||
|
import info.nightscout.androidaps.database.data.Block
|
||||||
|
import info.nightscout.androidaps.extensions.blockValueBySeconds
|
||||||
|
import info.nightscout.androidaps.extensions.pureProfileFromJson
|
||||||
|
import info.nightscout.androidaps.interfaces.*
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.Round
|
||||||
|
import info.nightscout.androidaps.utils.T
|
||||||
|
import info.nightscout.shared.SafeParse
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector: HasAndroidInjector) {
|
||||||
|
|
||||||
|
@Inject lateinit var activePlugin: ActivePlugin
|
||||||
|
@Inject lateinit var sp: SP
|
||||||
|
@Inject lateinit var profileFunction: ProfileFunction
|
||||||
|
@Inject lateinit var dateUtil: DateUtil
|
||||||
|
@Inject lateinit var config: Config
|
||||||
|
@Inject lateinit var rxBus: RxBus
|
||||||
|
@Inject lateinit var rh: ResourceHelper
|
||||||
|
|
||||||
|
var profile: ProfileSealed
|
||||||
|
var circadianProfile: ProfileSealed
|
||||||
|
lateinit var pumpProfile: ProfileSealed
|
||||||
|
var profilename: String = ""
|
||||||
|
var basal = DoubleArray(24)
|
||||||
|
var basalUntuned = IntArray(24)
|
||||||
|
var ic = 0.0
|
||||||
|
var isf = 0.0
|
||||||
|
var dia = 0.0
|
||||||
|
var peak = 0
|
||||||
|
var isValid: Boolean = false
|
||||||
|
var from: Long = 0
|
||||||
|
var pumpProfileAvgISF = 0.0
|
||||||
|
var pumpProfileAvgIC = 0.0
|
||||||
|
|
||||||
|
val icSize: Int
|
||||||
|
get() = profile.getIcsValues().size
|
||||||
|
val isfSize: Int
|
||||||
|
get() = profile.getIsfsMgdlValues().size
|
||||||
|
val avgISF: Double
|
||||||
|
get() = if (profile.getIsfsMgdlValues().size == 1) profile.getIsfsMgdlValues().get(0).value else Round.roundTo(averageProfileValue(profile.getIsfsMgdlValues()), 0.01)
|
||||||
|
val avgIC: Double
|
||||||
|
get() = if (profile.getIcsValues().size == 1) profile.getIcsValues().get(0).value else Round.roundTo(averageProfileValue(profile.getIcsValues()), 0.01)
|
||||||
|
|
||||||
|
fun getBasal(timestamp: Long): Double = basal[Profile.secondsFromMidnight(timestamp)/3600]
|
||||||
|
|
||||||
|
// for localProfilePlugin Synchronisation
|
||||||
|
fun basal() = jsonArray(basal)
|
||||||
|
fun ic(circadian: Boolean = false): JSONArray {
|
||||||
|
if(circadian)
|
||||||
|
return jsonArray(pumpProfile.icBlocks, avgIC/pumpProfileAvgIC)
|
||||||
|
return jsonArray(ic)
|
||||||
|
}
|
||||||
|
fun isf(circadian: Boolean = false): JSONArray {
|
||||||
|
if(circadian)
|
||||||
|
return jsonArray(pumpProfile.isfBlocks, avgISF/pumpProfileAvgISF)
|
||||||
|
return jsonArray(Profile.fromMgdlToUnits(isf, profile.units))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProfile(circadian: Boolean = false): PureProfile {
|
||||||
|
return if (circadian)
|
||||||
|
circadianProfile.convertToNonCustomizedProfile(dateUtil)
|
||||||
|
else
|
||||||
|
profile.convertToNonCustomizedProfile(dateUtil)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProfile() {
|
||||||
|
data()?.let { profile = ProfileSealed.Pure(it) }
|
||||||
|
data(true)?.let { circadianProfile = ProfileSealed.Pure(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
//Export json string with oref0 format used for autotune
|
||||||
|
// Include min_5m_carbimpact, insulin type, single value for carb_ratio and isf
|
||||||
|
fun profiletoOrefJSON(): String {
|
||||||
|
var jsonString = ""
|
||||||
|
val json = JSONObject()
|
||||||
|
val insulinInterface: Insulin = activePlugin.activeInsulin
|
||||||
|
try {
|
||||||
|
json.put("name", profilename)
|
||||||
|
json.put("min_5m_carbimpact", sp.getDouble("openapsama_min_5m_carbimpact", 3.0))
|
||||||
|
json.put("dia", dia)
|
||||||
|
if (insulinInterface.id === Insulin.InsulinType.OREF_ULTRA_RAPID_ACTING) json.put(
|
||||||
|
"curve",
|
||||||
|
"ultra-rapid"
|
||||||
|
) else if (insulinInterface.id === Insulin.InsulinType.OREF_RAPID_ACTING) json.put("curve", "rapid-acting") else if (insulinInterface.id === Insulin.InsulinType.OREF_LYUMJEV) {
|
||||||
|
json.put("curve", "ultra-rapid")
|
||||||
|
json.put("useCustomPeakTime", true)
|
||||||
|
json.put("insulinPeakTime", 45)
|
||||||
|
} else if (insulinInterface.id === Insulin.InsulinType.OREF_FREE_PEAK) {
|
||||||
|
val peaktime: Int = sp.getInt(rh.gs(R.string.key_insulin_oref_peak), 75)
|
||||||
|
json.put("curve", if (peaktime > 50) "rapid-acting" else "ultra-rapid")
|
||||||
|
json.put("useCustomPeakTime", true)
|
||||||
|
json.put("insulinPeakTime", peaktime)
|
||||||
|
}
|
||||||
|
val basals = JSONArray()
|
||||||
|
for (h in 0..23) {
|
||||||
|
val secondfrommidnight = h * 60 * 60
|
||||||
|
var time: String
|
||||||
|
time = DecimalFormat("00").format(h) + ":00:00"
|
||||||
|
basals.put(
|
||||||
|
JSONObject()
|
||||||
|
.put("start", time)
|
||||||
|
.put("minutes", h * 60)
|
||||||
|
.put("rate", profile.getBasalTimeFromMidnight(secondfrommidnight)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
json.put("basalprofile", basals)
|
||||||
|
val isfvalue = Round.roundTo(avgISF, 0.001)
|
||||||
|
json.put(
|
||||||
|
"isfProfile",
|
||||||
|
JSONObject().put(
|
||||||
|
"sensitivities",
|
||||||
|
JSONArray().put(JSONObject().put("i", 0).put("start", "00:00:00").put("sensitivity", isfvalue).put("offset", 0).put("x", 0).put("endoffset", 1440))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
json.put("carb_ratio", avgIC)
|
||||||
|
json.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2")))
|
||||||
|
json.put("autosens_min", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_min, "0.7")))
|
||||||
|
json.put("units", GlucoseUnit.MGDL.asText)
|
||||||
|
json.put("timezone", TimeZone.getDefault().id)
|
||||||
|
jsonString = json.toString(2).replace("\\/", "/")
|
||||||
|
} catch (e: JSONException) {}
|
||||||
|
|
||||||
|
return jsonString
|
||||||
|
}
|
||||||
|
|
||||||
|
fun data(circadian: Boolean = false): PureProfile? {
|
||||||
|
val json: JSONObject = profile.toPureNsJson(dateUtil)
|
||||||
|
try {
|
||||||
|
json.put("dia", dia)
|
||||||
|
if (circadian) {
|
||||||
|
json.put("sens", jsonArray(pumpProfile.isfBlocks, avgISF/pumpProfileAvgISF))
|
||||||
|
json.put("carbratio", jsonArray(pumpProfile.icBlocks, avgIC/pumpProfileAvgIC))
|
||||||
|
} else {
|
||||||
|
json.put("sens", jsonArray(Profile.fromMgdlToUnits(isf, profile.units)))
|
||||||
|
json.put("carbratio", jsonArray(ic))
|
||||||
|
}
|
||||||
|
json.put("basal", jsonArray(basal))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
return pureProfileFromJson(json, dateUtil, profile.units.asText)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun profileStore(circadian: Boolean = false): ProfileStore?
|
||||||
|
{
|
||||||
|
var profileStore: ProfileStore? = null
|
||||||
|
val json = JSONObject()
|
||||||
|
val store = JSONObject()
|
||||||
|
val tunedProfile = if (circadian) circadianProfile else profile
|
||||||
|
if (profilename.isEmpty())
|
||||||
|
profilename = rh.gs(R.string.autotune_tunedprofile_name)
|
||||||
|
try {
|
||||||
|
store.put(profilename, tunedProfile.toPureNsJson(dateUtil))
|
||||||
|
json.put("defaultProfile", profilename)
|
||||||
|
json.put("store", store)
|
||||||
|
json.put("startDate", dateUtil.toISOAsUTC(dateUtil.now()))
|
||||||
|
profileStore = ProfileStore(injector, json, dateUtil)
|
||||||
|
} catch (e: JSONException) { }
|
||||||
|
return profileStore
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jsonArray(values: DoubleArray): JSONArray {
|
||||||
|
val json = JSONArray()
|
||||||
|
for (h in 0..23) {
|
||||||
|
val secondfrommidnight = h * 60 * 60
|
||||||
|
val df = DecimalFormat("00")
|
||||||
|
val time = df.format(h.toLong()) + ":00"
|
||||||
|
json.put(
|
||||||
|
JSONObject()
|
||||||
|
.put("time", time)
|
||||||
|
.put("timeAsSeconds", secondfrommidnight)
|
||||||
|
.put("value", values[h])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jsonArray(value: Double) =
|
||||||
|
JSONArray().put(
|
||||||
|
JSONObject()
|
||||||
|
.put("time", "00:00")
|
||||||
|
.put("timeAsSeconds", 0)
|
||||||
|
.put("value", value)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun jsonArray(values: List<Block>, multiplier: Double = 1.0): JSONArray {
|
||||||
|
val json = JSONArray()
|
||||||
|
var elapsedHours = 0L
|
||||||
|
values.forEach {
|
||||||
|
val value = values.blockValueBySeconds(T.hours(elapsedHours).secs().toInt(), multiplier, 0)
|
||||||
|
json.put(
|
||||||
|
JSONObject()
|
||||||
|
.put("time", DecimalFormat("00").format(elapsedHours) + ":00")
|
||||||
|
.put("timeAsSeconds", T.hours(elapsedHours).secs())
|
||||||
|
.put("value", value)
|
||||||
|
)
|
||||||
|
elapsedHours += T.msecs(it.duration).hours()
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun averageProfileValue(pf: Array<Profile.ProfileValue>?): Double {
|
||||||
|
var avgValue = 0.0
|
||||||
|
val secondPerDay = 24 * 60 * 60
|
||||||
|
if (pf == null) return avgValue
|
||||||
|
for (i in pf.indices) {
|
||||||
|
avgValue += pf[i].value * ((if (i == pf.size - 1) secondPerDay else pf[i + 1].timeAsSeconds) - pf[i].timeAsSeconds)
|
||||||
|
}
|
||||||
|
avgValue /= secondPerDay.toDouble()
|
||||||
|
return avgValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
injector.androidInjector().inject(this)
|
||||||
|
this.profile = profile as ProfileSealed
|
||||||
|
circadianProfile = profile
|
||||||
|
isValid = profile.isValid
|
||||||
|
if (isValid) {
|
||||||
|
//initialize tuned value with current profile values
|
||||||
|
for (h in 0..23) {
|
||||||
|
basal[h] = Round.roundTo(profile.basalBlocks.blockValueBySeconds(T.hours(h.toLong()).secs().toInt(), 1.0, 0), 0.001)
|
||||||
|
}
|
||||||
|
ic = avgIC
|
||||||
|
isf = avgISF
|
||||||
|
pumpProfile = profile
|
||||||
|
pumpProfileAvgIC = avgIC
|
||||||
|
pumpProfileAvgISF = avgISF
|
||||||
|
}
|
||||||
|
dia = localInsulin.dia
|
||||||
|
peak = localInsulin.peak
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune.data
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.database.entities.GlucoseValue.TrendArrow
|
||||||
|
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.T
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Rumen Georgiev on 2/24/2018.
|
||||||
|
*/
|
||||||
|
class BGDatum {
|
||||||
|
//Added by Rumen for autotune
|
||||||
|
var id: Long = 0
|
||||||
|
var date = 0L
|
||||||
|
var value = 0.0
|
||||||
|
var direction: TrendArrow? = null
|
||||||
|
var deviation = 0.0
|
||||||
|
var bgi = 0.0
|
||||||
|
var mealAbsorption = ""
|
||||||
|
var mealCarbs = 0
|
||||||
|
var uamAbsorption = ""
|
||||||
|
var avgDelta = 0.0
|
||||||
|
var bgReading: GlucoseValue? = null
|
||||||
|
private set
|
||||||
|
var dateUtil: DateUtil
|
||||||
|
|
||||||
|
constructor(dateUtil: DateUtil) { this.dateUtil = dateUtil}
|
||||||
|
constructor(json: JSONObject, dateUtil: DateUtil) {
|
||||||
|
this.dateUtil = dateUtil
|
||||||
|
try {
|
||||||
|
if (json.has("_id")) id = json.getLong("_id")
|
||||||
|
if (json.has("date")) date = json.getLong("date")
|
||||||
|
if (json.has("sgv")) value = json.getDouble("sgv")
|
||||||
|
if (json.has("direction")) direction = TrendArrow.fromString(json.getString("direction"))
|
||||||
|
if (json.has("deviation")) deviation = json.getDouble("deviation")
|
||||||
|
if (json.has("BGI")) bgi = json.getDouble("BGI")
|
||||||
|
if (json.has("avgDelta")) avgDelta = json.getDouble("avgDelta")
|
||||||
|
if (json.has("mealAbsorption")) mealAbsorption = json.getString("mealAbsorption")
|
||||||
|
if (json.has("mealCarbs")) mealCarbs = json.getInt("mealCarbs")
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(glucoseValue: GlucoseValue, dateUtil: DateUtil) {
|
||||||
|
this.dateUtil = dateUtil
|
||||||
|
date = glucoseValue.timestamp
|
||||||
|
value = glucoseValue.value
|
||||||
|
direction = glucoseValue.trendArrow
|
||||||
|
id = glucoseValue.id
|
||||||
|
this.bgReading = glucoseValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJSON(mealData: Boolean): JSONObject {
|
||||||
|
val bgjson = JSONObject()
|
||||||
|
val utcOffset = T.msecs(TimeZone.getDefault().getOffset(dateUtil.now()).toLong()).hours()
|
||||||
|
try {
|
||||||
|
bgjson.put("_id", id)
|
||||||
|
bgjson.put("date", date)
|
||||||
|
bgjson.put("dateString", dateUtil.toISOAsUTC(date))
|
||||||
|
bgjson.put("sgv", value)
|
||||||
|
bgjson.put("direction", direction)
|
||||||
|
bgjson.put("type", "sgv")
|
||||||
|
bgjson.put("sysTime", dateUtil.toISOAsUTC(date))
|
||||||
|
bgjson.put("utcOffset", utcOffset)
|
||||||
|
bgjson.put("glucose", value)
|
||||||
|
bgjson.put("avgDelta", avgDelta)
|
||||||
|
bgjson.put("BGI", bgi)
|
||||||
|
bgjson.put("deviation", deviation)
|
||||||
|
if (mealData) {
|
||||||
|
bgjson.put("mealAbsorption", mealAbsorption)
|
||||||
|
bgjson.put("mealCarbs", mealCarbs)
|
||||||
|
}
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
return bgjson
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune.data
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Rumen Georgiev on 2/26/2018.
|
||||||
|
*/
|
||||||
|
class CRDatum {
|
||||||
|
|
||||||
|
var crInitialIOB = 0.0
|
||||||
|
var crInitialBG = 0.0
|
||||||
|
var crInitialCarbTime = 0L
|
||||||
|
var crEndIOB = 0.0
|
||||||
|
var crEndBG = 0.0
|
||||||
|
var crEndTime = 0L
|
||||||
|
var crCarbs = 0.0
|
||||||
|
var crInsulin = 0.0
|
||||||
|
var crInsulinTotal = 0.0
|
||||||
|
var dateUtil: DateUtil
|
||||||
|
|
||||||
|
constructor(dateUtil: DateUtil) { this.dateUtil = dateUtil}
|
||||||
|
constructor(json: JSONObject, dateUtil: DateUtil) {
|
||||||
|
this.dateUtil = dateUtil
|
||||||
|
try {
|
||||||
|
if (json.has("CRInitialIOB")) crInitialIOB = json.getDouble("CRInitialIOB")
|
||||||
|
if (json.has("CRInitialBG")) crInitialBG = json.getDouble("CRInitialBG")
|
||||||
|
if (json.has("CRInitialCarbTime")) crInitialCarbTime = dateUtil.fromISODateString(json.getString("CRInitialCarbTime"))
|
||||||
|
if (json.has("CREndIOB")) crEndIOB = json.getDouble("CREndIOB")
|
||||||
|
if (json.has("CREndBG")) crEndBG = json.getDouble("CREndBG")
|
||||||
|
if (json.has("CREndTime")) crEndTime = dateUtil.fromISODateString(json.getString("CREndTime"))
|
||||||
|
if (json.has("CRCarbs")) crCarbs = json.getDouble("CRCarbs")
|
||||||
|
if (json.has("CRInsulin")) crInsulin = json.getDouble("CRInsulin")
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJSON(): JSONObject {
|
||||||
|
val crjson = JSONObject()
|
||||||
|
try {
|
||||||
|
crjson.put("CRInitialIOB", crInitialIOB)
|
||||||
|
crjson.put("CRInitialBG", crInitialBG.toInt())
|
||||||
|
crjson.put("CRInitialCarbTime", dateUtil.toISOString(crInitialCarbTime))
|
||||||
|
crjson.put("CREndIOB", crEndIOB)
|
||||||
|
crjson.put("CREndBG", crEndBG.toInt())
|
||||||
|
crjson.put("CREndTime", dateUtil.toISOString(crEndTime))
|
||||||
|
crjson.put("CRCarbs", crCarbs.toInt())
|
||||||
|
crjson.put("CRInsulin", crInsulin)
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
return crjson
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune.data
|
||||||
|
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class DiaDeviation(var dia: Double = 0.0, var meanDeviation: Double = 0.0, var smrDeviation: Double = 0.0, var rmsDeviation: Double = 0.0) {
|
||||||
|
|
||||||
|
constructor(json: JSONObject) : this() {
|
||||||
|
try {
|
||||||
|
if (json.has("dia")) dia = json.getDouble("dia")
|
||||||
|
if (json.has("meanDeviation")) meanDeviation = json.getDouble("meanDeviation")
|
||||||
|
if (json.has("SMRDeviation")) smrDeviation = json.getDouble("SMRDeviation")
|
||||||
|
if (json.has("RMSDeviation")) rmsDeviation = json.getDouble("RMSDeviation")
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJSON(): JSONObject {
|
||||||
|
val crjson = JSONObject()
|
||||||
|
try {
|
||||||
|
crjson.put("dia", dia)
|
||||||
|
crjson.put("meanDeviation", meanDeviation.toInt())
|
||||||
|
crjson.put("SMRDeviation", smrDeviation)
|
||||||
|
crjson.put("RMSDeviation", rmsDeviation.toInt())
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
return crjson
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune.data
|
||||||
|
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class PeakDeviation(var peak: Int = 0, var meanDeviation: Double = 0.0, var smrDeviation: Double = 0.0, var rmsDeviation: Double = 0.0) {
|
||||||
|
|
||||||
|
constructor(json: JSONObject) : this() {
|
||||||
|
try {
|
||||||
|
if (json.has("peak")) peak = json.getInt("peak")
|
||||||
|
if (json.has("meanDeviation")) meanDeviation = json.getDouble("meanDeviation")
|
||||||
|
if (json.has("SMRDeviation")) smrDeviation = json.getDouble("SMRDeviation")
|
||||||
|
if (json.has("RMSDeviation")) rmsDeviation = json.getDouble("RMSDeviation")
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJSON(): JSONObject {
|
||||||
|
val crjson = JSONObject()
|
||||||
|
try {
|
||||||
|
crjson.put("peak", peak)
|
||||||
|
crjson.put("meanDeviation", meanDeviation.toInt())
|
||||||
|
crjson.put("SMRDeviation", smrDeviation)
|
||||||
|
crjson.put("RMSDeviation", rmsDeviation.toInt())
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
return crjson
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune.data
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class PreppedGlucose {
|
||||||
|
|
||||||
|
var crData: List<CRDatum> = ArrayList()
|
||||||
|
var csfGlucoseData: List<BGDatum> = ArrayList()
|
||||||
|
var isfGlucoseData: List<BGDatum> = ArrayList()
|
||||||
|
var basalGlucoseData: List<BGDatum> = ArrayList()
|
||||||
|
var diaDeviations: List<DiaDeviation> = ArrayList()
|
||||||
|
var peakDeviations: List<PeakDeviation> = ArrayList()
|
||||||
|
var from: Long = 0
|
||||||
|
lateinit var dateUtil: DateUtil
|
||||||
|
|
||||||
|
// to generate same king of json string than oref0-autotune-prep
|
||||||
|
override fun toString(): String {
|
||||||
|
return toString(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(from: Long, crData: List<CRDatum>, csfGlucoseData: List<BGDatum>, isfGlucoseData: List<BGDatum>, basalGlucoseData: List<BGDatum>, dateUtil: DateUtil) {
|
||||||
|
this.from = from
|
||||||
|
this.crData = crData
|
||||||
|
this.csfGlucoseData = csfGlucoseData
|
||||||
|
this.isfGlucoseData = isfGlucoseData
|
||||||
|
this.basalGlucoseData = basalGlucoseData
|
||||||
|
this.dateUtil = dateUtil
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(json: JSONObject?, dateUtil: DateUtil) {
|
||||||
|
if (json == null) return
|
||||||
|
this.dateUtil = dateUtil
|
||||||
|
crData = ArrayList()
|
||||||
|
csfGlucoseData = ArrayList()
|
||||||
|
isfGlucoseData = ArrayList()
|
||||||
|
basalGlucoseData = ArrayList()
|
||||||
|
try {
|
||||||
|
crData = JsonCRDataToList(json.getJSONArray("CRData"))
|
||||||
|
csfGlucoseData = JsonGlucoseDataToList(json.getJSONArray("CSFGlucoseData"))
|
||||||
|
isfGlucoseData = JsonGlucoseDataToList(json.getJSONArray("ISFGlucoseData"))
|
||||||
|
basalGlucoseData = JsonGlucoseDataToList(json.getJSONArray("basalGlucoseData"))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsonGlucoseDataToList(array: JSONArray): List<BGDatum> {
|
||||||
|
val bgData: MutableList<BGDatum> = ArrayList()
|
||||||
|
for (index in 0 until array.length()) {
|
||||||
|
try {
|
||||||
|
val o = array.getJSONObject(index)
|
||||||
|
bgData.add(BGDatum(o, dateUtil))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bgData
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsonCRDataToList(array: JSONArray): List<CRDatum> {
|
||||||
|
val crData: MutableList<CRDatum> = ArrayList()
|
||||||
|
for (index in 0 until array.length()) {
|
||||||
|
try {
|
||||||
|
val o = array.getJSONObject(index)
|
||||||
|
crData.add(CRDatum(o, dateUtil))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crData
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(indent: Int): String {
|
||||||
|
var jsonString = ""
|
||||||
|
val json = JSONObject()
|
||||||
|
try {
|
||||||
|
val crjson = JSONArray()
|
||||||
|
for (crd in crData) {
|
||||||
|
crjson.put(crd.toJSON())
|
||||||
|
}
|
||||||
|
val csfjson = JSONArray()
|
||||||
|
for (bgd in csfGlucoseData) {
|
||||||
|
csfjson.put(bgd.toJSON(true))
|
||||||
|
}
|
||||||
|
val isfjson = JSONArray()
|
||||||
|
for (bgd in isfGlucoseData) {
|
||||||
|
isfjson.put(bgd.toJSON(false))
|
||||||
|
}
|
||||||
|
val basaljson = JSONArray()
|
||||||
|
for (bgd in basalGlucoseData) {
|
||||||
|
basaljson.put(bgd.toJSON(false))
|
||||||
|
}
|
||||||
|
val diajson = JSONArray()
|
||||||
|
val peakjson = JSONArray()
|
||||||
|
if (diaDeviations.size > 0 || peakDeviations.size > 0) {
|
||||||
|
for (diad in diaDeviations) {
|
||||||
|
diajson.put(diad.toJSON())
|
||||||
|
}
|
||||||
|
for (peakd in peakDeviations) {
|
||||||
|
peakjson.put(peakd.toJSON())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json.put("CRData", crjson)
|
||||||
|
json.put("CSFGlucoseData", csfjson)
|
||||||
|
json.put("ISFGlucoseData", isfjson)
|
||||||
|
json.put("basalGlucoseData", basaljson)
|
||||||
|
if (diaDeviations.size > 0 || peakDeviations.size > 0) {
|
||||||
|
json.put("diaDeviations", diajson)
|
||||||
|
json.put("peakDeviations", peakjson)
|
||||||
|
}
|
||||||
|
jsonString = if (indent != 0) json.toString(indent) else json.toString()
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
}
|
||||||
|
return jsonString
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.autotune.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventAutotuneUpdateGui : Event()
|
|
@ -69,11 +69,24 @@ class MaintenancePlugin @Inject constructor(
|
||||||
val files = logDir.listFiles { _: File?, name: String ->
|
val files = logDir.listFiles { _: File?, name: String ->
|
||||||
(name.startsWith("AndroidAPS") && name.endsWith(".zip"))
|
(name.startsWith("AndroidAPS") && name.endsWith(".zip"))
|
||||||
}
|
}
|
||||||
|
val autotunefiles = logDir.listFiles { _: File?, name: String ->
|
||||||
|
(name.startsWith("autotune") && name.endsWith(".zip"))
|
||||||
|
}
|
||||||
|
val amount = sp.getInt(R.string.key_logshipper_amount, keep)
|
||||||
|
val keepIndex = amount - 1
|
||||||
|
if (autotunefiles != null && autotunefiles.isNotEmpty()) {
|
||||||
|
Arrays.sort(autotunefiles) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
|
||||||
|
var delAutotuneFiles = listOf(*autotunefiles)
|
||||||
|
if (keepIndex < delAutotuneFiles.size) {
|
||||||
|
delAutotuneFiles = delAutotuneFiles.subList(keepIndex, delAutotuneFiles.size)
|
||||||
|
for (file in delAutotuneFiles) {
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (files == null || files.isEmpty()) return
|
if (files == null || files.isEmpty()) return
|
||||||
Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
|
Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
|
||||||
var delFiles = listOf(*files)
|
var delFiles = listOf(*files)
|
||||||
val amount = sp.getInt(R.string.key_logshipper_amount, keep)
|
|
||||||
val keepIndex = amount - 1
|
|
||||||
if (keepIndex < delFiles.size) {
|
if (keepIndex < delFiles.size) {
|
||||||
delFiles = delFiles.subList(keepIndex, delFiles.size)
|
delFiles = delFiles.subList(keepIndex, delFiles.size)
|
||||||
for (file in delFiles) {
|
for (file in delFiles) {
|
||||||
|
|
343
app/src/main/res/layout/autotune_fragment.xml
Normal file
343
app/src/main/res/layout/autotune_fragment.xml
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".plugins.general.autotune.AutotuneFragment">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/Widget.MaterialComponents.CardView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
app:cardCornerRadius="4dp"
|
||||||
|
app:contentPadding="2dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardUseCompatPadding="false"
|
||||||
|
android:layout_gravity="center">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal|center_vertical"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="end"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:text="@string/autotune_profile"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/autotune_select_profile"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||||
|
android:id="@+id/profileList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal|center_vertical"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="end"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:text="@string/autotune_tune_days"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
|
||||||
|
<info.nightscout.androidaps.utils.ui.NumberPicker
|
||||||
|
android:id="@+id/tune_days"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
app:customContentDescription="@string/careportal_newnstreatment_duration_label" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/Widget.MaterialComponents.CardView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
app:cardCornerRadius="4dp"
|
||||||
|
app:contentPadding="2dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardUseCompatPadding="false"
|
||||||
|
android:layout_gravity="center">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="end"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:text="@string/autotune_last_run"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tune_lastrun"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="start"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:gravity="end"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:text="@string/autotune_warning"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tune_warning"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="start"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/Widget.MaterialComponents.CardView"
|
||||||
|
android:id="@+id/autotune_results_card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
app:cardCornerRadius="4dp"
|
||||||
|
app:contentPadding="2dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardUseCompatPadding="false"
|
||||||
|
android:layout_gravity="center">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/autotune_results"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.gridlayout.widget.GridLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="5dip"
|
||||||
|
app:columnCount="2">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/autotune_profileswitch"
|
||||||
|
style="@style/GrayButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/activate_profile"
|
||||||
|
app:icon="@drawable/ic_local_activate"
|
||||||
|
app:iconPadding="-6dp"
|
||||||
|
app:iconTint="@color/ic_local_activate"
|
||||||
|
app:layout_column="0"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_gravity="fill"
|
||||||
|
app:layout_row="0" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/autotune_compare"
|
||||||
|
style="@style/GrayButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/autotune_compare_profile"
|
||||||
|
app:icon="@drawable/ic_compare_profiles"
|
||||||
|
app:iconPadding="-6dp"
|
||||||
|
app:iconTintMode="multiply"
|
||||||
|
app:layout_column="1"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_gravity="fill"
|
||||||
|
app:layout_row="0" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/autotune_copylocal"
|
||||||
|
style="@style/GrayButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/autotune_copy_localprofile_button"
|
||||||
|
app:icon="@drawable/ic_clone_48"
|
||||||
|
app:iconPadding="-6dp"
|
||||||
|
app:iconTintMode="multiply"
|
||||||
|
app:layout_column="0"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_gravity="fill"
|
||||||
|
app:layout_row="1" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/autotune_update_profile"
|
||||||
|
style="@style/GrayButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/autotune_update_input_profile_button"
|
||||||
|
app:icon="@drawable/ic_local_save"
|
||||||
|
app:iconPadding="-8dp"
|
||||||
|
app:iconTint="@color/ic_local_save"
|
||||||
|
app:layout_column="1"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_gravity="fill"
|
||||||
|
app:layout_row="1" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/autotune_revert_profile"
|
||||||
|
style="@style/GrayButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/autotune_revert_input_profile_button"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:icon="@drawable/ic_local_reset"
|
||||||
|
app:iconPadding="-6dp"
|
||||||
|
app:iconTint="@color/ic_local_reset"
|
||||||
|
app:layout_column="1"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_gravity="fill"
|
||||||
|
app:layout_row="1" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/autotune_run"
|
||||||
|
style="@style/GrayButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:text="@string/autotune_run"
|
||||||
|
app:icon="@drawable/ic_autotune"
|
||||||
|
app:iconPadding="-4dp"
|
||||||
|
app:iconTint="@color/ic_local_save"
|
||||||
|
app:layout_column="0"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_gravity="fill"
|
||||||
|
app:layout_row="2" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/autotune_check_input_profile"
|
||||||
|
style="@style/GrayButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:text="@string/autotune_check_input_profile_button"
|
||||||
|
app:icon="@drawable/ic_home_profile"
|
||||||
|
app:iconPadding="-6dp"
|
||||||
|
app:iconTint="?attr/profileColor"
|
||||||
|
app:layout_column="1"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_gravity="fill"
|
||||||
|
app:layout_row="2" />
|
||||||
|
|
||||||
|
</androidx.gridlayout.widget.GridLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
|
@ -442,6 +442,7 @@
|
||||||
<string name="enableuam_summary">Detekce neoznámených jídel</string>
|
<string name="enableuam_summary">Detekce neoznámených jídel</string>
|
||||||
<string name="insulin_oref_peak">Čas vrcholu IOB křivky</string>
|
<string name="insulin_oref_peak">Čas vrcholu IOB křivky</string>
|
||||||
<string name="insulin_peak_time">Vrchol křivky [min]</string>
|
<string name="insulin_peak_time">Vrchol křivky [min]</string>
|
||||||
|
<string name="insulin_peak">Vrchol</string>
|
||||||
<string name="free_peak_oref">Volitelný vrchol - Oref</string>
|
<string name="free_peak_oref">Volitelný vrchol - Oref</string>
|
||||||
<string name="rapid_acting_oref">Rychle působící - Oref</string>
|
<string name="rapid_acting_oref">Rychle působící - Oref</string>
|
||||||
<string name="ultrarapid_oref">Ultra rychlý - Oref</string>
|
<string name="ultrarapid_oref">Ultra rychlý - Oref</string>
|
||||||
|
|
|
@ -104,9 +104,11 @@
|
||||||
<string name="troubleshooting_wheretoask">Hvor kan du søge efter hjælp til AndroidAPS?</string>
|
<string name="troubleshooting_wheretoask">Hvor kan du søge efter hjælp til AndroidAPS?</string>
|
||||||
<string name="troubleshooting_fb">Du kan bede om råd i \"AndroidAPS-users\" Facebook-gruppen.</string>
|
<string name="troubleshooting_fb">Du kan bede om råd i \"AndroidAPS-users\" Facebook-gruppen.</string>
|
||||||
<string name="troubleshooting_wiki">Du bør læse (og genlæse) AndroidAPS dokumentation.</string>
|
<string name="troubleshooting_wiki">Du bør læse (og genlæse) AndroidAPS dokumentation.</string>
|
||||||
|
<string name="troubleshooting_gitter">Du kan bede om råd og logge tekniske problemer eller andre problemer i AndroidAPS Discord.</string>
|
||||||
<string name="troubleshooting_yourendo">Du bør spørge din diabetessygeplejerske/endokrinolog.</string>
|
<string name="troubleshooting_yourendo">Du bør spørge din diabetessygeplejerske/endokrinolog.</string>
|
||||||
<string name="troubleshooting_hint1">https://androidaps.readthedocs.io/en/latest/EN/Installing-AndroidAPS/Update-to-new-version.html#troubleshooting</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_hint2">https://www.facebook.com/groups/AndroidAPSUsers/</string>
|
||||||
|
<string name="troubleshooting_hint3">https://discord.gg/4fQUWHZ4Mw</string>
|
||||||
<string name="insulin_label">Insulin Plugins</string>
|
<string name="insulin_label">Insulin Plugins</string>
|
||||||
<string name="insulin_ultrarapid">Hvilken insulin skal du bruge sammen med Ultra-Rapid Oref-pluginnet?</string>
|
<string name="insulin_ultrarapid">Hvilken insulin skal du bruge sammen med Ultra-Rapid Oref-pluginnet?</string>
|
||||||
<string name="insulin_fiasp">Fiasp®</string>
|
<string name="insulin_fiasp">Fiasp®</string>
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<string name="description_ns_client">Synkroniserer dine data med NightScout</string>
|
<string name="description_ns_client">Synkroniserer dine data med NightScout</string>
|
||||||
<string name="description_ama">Status for algoritmen i 2017</string>
|
<string name="description_ama">Status for algoritmen i 2017</string>
|
||||||
<string name="description_smb">Seneste algoritme for avancerede brugere</string>
|
<string name="description_smb">Seneste algoritme for avancerede brugere</string>
|
||||||
|
<string name="description_smb_dynamic_isf">Seneste algoritme for avancerede brugere med dynamisk/automatisk ISF</string>
|
||||||
<string name="description_overview">Viser den aktuelle tilstand af dit loop og knapper til de mest almindelige handlinger</string>
|
<string name="description_overview">Viser den aktuelle tilstand af dit loop og knapper til de mest almindelige handlinger</string>
|
||||||
<string name="description_persistent_notification">Viser en løbende notifikation med en kort oversigt over, hvad dit loop gør</string>
|
<string name="description_persistent_notification">Viser en løbende notifikation med en kort oversigt over, hvad dit loop gør</string>
|
||||||
<string name="description_profile_local">Definér en profil, der er tilgængelig offline.</string>
|
<string name="description_profile_local">Definér en profil, der er tilgængelig offline.</string>
|
||||||
|
@ -160,6 +161,7 @@
|
||||||
<string name="end_user_license_agreement">Slutbrugerlicensaftale</string>
|
<string name="end_user_license_agreement">Slutbrugerlicensaftale</string>
|
||||||
<string name="end_user_license_agreement_text">MÅ IKKE BRUGES TIL AT TRÆFFE MEDICINSKE BESLUTNINGER. DER ER INGEN GARANTI FOR PROGRAMMET, I DET OMFANG GÆLDENDE LOV TILLADER DET. UNDTAGEN NÅR DET ELLERS ER ANFØRT SKRIFTLIGT, AT RETTIGHEDSHAVERE OG / ELLER ANDRE PARTER LEVERER PROGRAMMET \"SOM BESET\" UDEN NOGEN FORM FOR GARANTI, HVERKEN UDTRYKT ELLER UNDERFORSTÅET, HERUNDER, MEN IKKE BEGRÆNSET TIL, DE UNDERFORSTÅELIGHEDER, DER ER FORBUNDET MED EGNETHED OG EGNETHED TIL ET BESTEMT FORMÅL. HELE RISIKOEN MED HENSYN TIL KVALITETEN OG YDEEVNEN AF PROGRAMMET ER DIN. HVIS PROGRAMMET VISER SIG AT VÆRE DEFEKT, BÆRER DU SELV OMKOSTNINGERNE VED ALLE NØDVENDIGE SERVICER, REPARATIONER ELLER KORREKTIONER SOM ER NØDVENDIGE.</string>
|
<string name="end_user_license_agreement_text">MÅ IKKE BRUGES TIL AT TRÆFFE MEDICINSKE BESLUTNINGER. DER ER INGEN GARANTI FOR PROGRAMMET, I DET OMFANG GÆLDENDE LOV TILLADER DET. UNDTAGEN NÅR DET ELLERS ER ANFØRT SKRIFTLIGT, AT RETTIGHEDSHAVERE OG / ELLER ANDRE PARTER LEVERER PROGRAMMET \"SOM BESET\" UDEN NOGEN FORM FOR GARANTI, HVERKEN UDTRYKT ELLER UNDERFORSTÅET, HERUNDER, MEN IKKE BEGRÆNSET TIL, DE UNDERFORSTÅELIGHEDER, DER ER FORBUNDET MED EGNETHED OG EGNETHED TIL ET BESTEMT FORMÅL. HELE RISIKOEN MED HENSYN TIL KVALITETEN OG YDEEVNEN AF PROGRAMMET ER DIN. HVIS PROGRAMMET VISER SIG AT VÆRE DEFEKT, BÆRER DU SELV OMKOSTNINGERNE VED ALLE NØDVENDIGE SERVICER, REPARATIONER ELLER KORREKTIONER SOM ER NØDVENDIGE.</string>
|
||||||
<string name="end_user_license_agreement_i_understand">JEG FORSTÅR OG ER ENIG</string>
|
<string name="end_user_license_agreement_i_understand">JEG FORSTÅR OG ER ENIG</string>
|
||||||
|
<string name="save">Gem</string>
|
||||||
<string name="reloadprofile">Genindlæs profil</string>
|
<string name="reloadprofile">Genindlæs profil</string>
|
||||||
<string name="smscommunicator">SMS Kommunikator</string>
|
<string name="smscommunicator">SMS Kommunikator</string>
|
||||||
<string name="smscommunicator_allowednumbers">Tilladte telefonnumre</string>
|
<string name="smscommunicator_allowednumbers">Tilladte telefonnumre</string>
|
||||||
|
@ -244,6 +246,7 @@
|
||||||
<string name="wear">Ur</string>
|
<string name="wear">Ur</string>
|
||||||
<string name="resend_all_data">Send alle data igen</string>
|
<string name="resend_all_data">Send alle data igen</string>
|
||||||
<string name="open_settings_on_wear">Åbn indstillinger på ur</string>
|
<string name="open_settings_on_wear">Åbn indstillinger på ur</string>
|
||||||
|
<string name="basal_rate">Basalrate</string>
|
||||||
<string name="basalvaluebelowminimum">Basal værdi under minimum. Profil ikke angivet!</string>
|
<string name="basalvaluebelowminimum">Basal værdi under minimum. Profil ikke angivet!</string>
|
||||||
<string name="sms_actualbg">BG:</string>
|
<string name="sms_actualbg">BG:</string>
|
||||||
<string name="sms_lastbg">Sidste BG:</string>
|
<string name="sms_lastbg">Sidste BG:</string>
|
||||||
|
@ -264,6 +267,7 @@
|
||||||
<string name="configbuilder_shortname">KONF</string>
|
<string name="configbuilder_shortname">KONF</string>
|
||||||
<string name="loop_shortname">LOOP</string>
|
<string name="loop_shortname">LOOP</string>
|
||||||
<string name="oaps_shortname">OAPS</string>
|
<string name="oaps_shortname">OAPS</string>
|
||||||
|
<string name="dynisf_shortname">DynISF</string>
|
||||||
<string name="localprofile_shortname">LP</string>
|
<string name="localprofile_shortname">LP</string>
|
||||||
<string name="overview_shortname">HJEM</string>
|
<string name="overview_shortname">HJEM</string>
|
||||||
<string name="virtualpump_shortname">VPUMPE</string>
|
<string name="virtualpump_shortname">VPUMPE</string>
|
||||||
|
@ -429,6 +433,9 @@
|
||||||
<string name="ns_localbroadcasts">Aktiver udsendelse til andre apps (såsom xDrip+). Aktiver ikke hvis du har mere end én version af AAPS eller NSClient installeret!</string>
|
<string name="ns_localbroadcasts">Aktiver udsendelse til andre apps (såsom xDrip+). Aktiver ikke hvis du har mere end én version af AAPS eller NSClient installeret!</string>
|
||||||
<string name="ns_localbroadcasts_title">Aktiver lokale udsendelser.</string>
|
<string name="ns_localbroadcasts_title">Aktiver lokale udsendelser.</string>
|
||||||
<string name="openapssmb">OpenAPS SMB</string>
|
<string name="openapssmb">OpenAPS SMB</string>
|
||||||
|
<string name="openaps_smb_dynamic_isf">Dynamisk ISF</string>
|
||||||
|
<string name="DynISFAdjust_title">DynamicISF-justeringsfaktor %%</string>
|
||||||
|
<string name="DynISFAdjust_summary">Justeringsfaktor for Dynamisk ISF. Indstil mere end 100%% for mere aggressive korrektionsdoser og mindre end 100%% for mindre aggressive korrektioner.</string>
|
||||||
<string name="enableuam">Aktiver UAM</string>
|
<string name="enableuam">Aktiver UAM</string>
|
||||||
<string name="enablesmb">Aktiver SMB</string>
|
<string name="enablesmb">Aktiver SMB</string>
|
||||||
<string name="enablesmb_summary">Brug Super Mikro Boluser i stedet for midlertidig basal for hurtigere handling</string>
|
<string name="enablesmb_summary">Brug Super Mikro Boluser i stedet for midlertidig basal for hurtigere handling</string>
|
||||||
|
@ -496,6 +503,7 @@
|
||||||
<string name="negativeonly">Kun negative</string>
|
<string name="negativeonly">Kun negative</string>
|
||||||
<string name="overview_editquickwizard_usecob">COB beregning</string>
|
<string name="overview_editquickwizard_usecob">COB beregning</string>
|
||||||
<string name="overview_editquickwizard_usetemptarget">Midlertidig mål beregning</string>
|
<string name="overview_editquickwizard_usetemptarget">Midlertidig mål beregning</string>
|
||||||
|
<string name="overview_editquickwizard_usepercentage">Beregning i %</string>
|
||||||
<string name="loopenabled">Loop aktiveret</string>
|
<string name="loopenabled">Loop aktiveret</string>
|
||||||
<string name="apsselected">APS valgt</string>
|
<string name="apsselected">APS valgt</string>
|
||||||
<string name="nsclienthaswritepermission">NSClient har skrivetilladelse</string>
|
<string name="nsclienthaswritepermission">NSClient har skrivetilladelse</string>
|
||||||
|
@ -656,6 +664,10 @@
|
||||||
<string name="sensitivity_raises_target_title">Følsomhed hæver midlertidige mål</string>
|
<string name="sensitivity_raises_target_title">Følsomhed hæver midlertidige mål</string>
|
||||||
<string name="sensitivity_raises_target_summary">Når der påvises følsomhed, skal målglukosen hæves</string>
|
<string name="sensitivity_raises_target_summary">Når der påvises følsomhed, skal målglukosen hæves</string>
|
||||||
<string name="careportal_removestartedevents">Ren AndroidAPS startet</string>
|
<string name="careportal_removestartedevents">Ren AndroidAPS startet</string>
|
||||||
|
<string name="show_invalidated">Vis ugyldige</string>
|
||||||
|
<string name="hide_invalidated">Skjul ugyldige</string>
|
||||||
|
<string name="remove_items">Fjern elementer</string>
|
||||||
|
<string name="sort_items">Sortér elementer</string>
|
||||||
<string name="storedsettingsfound">Gemte indstillinger fundet</string>
|
<string name="storedsettingsfound">Gemte indstillinger fundet</string>
|
||||||
<string name="allow_hardware_pump_text">Bemærk: Hvis du aktiverer og opretter forbindelse til en hardwarepumpe, vil AndroidAPS kopiere de basale indstillinger fra profilen til pumpen, overskrive den eksisterende basal rate lagret på pumpen. Sørg for, at du har den korrekte basal indstilling i AndroidAPS. Hvis du ikke er sikker på eller ikke ønsker at overskrive de basale indstillinger på din pumpe, tryk på annuller og gentag skift til pumpen på et senere tidspunkt.</string>
|
<string name="allow_hardware_pump_text">Bemærk: Hvis du aktiverer og opretter forbindelse til en hardwarepumpe, vil AndroidAPS kopiere de basale indstillinger fra profilen til pumpen, overskrive den eksisterende basal rate lagret på pumpen. Sørg for, at du har den korrekte basal indstilling i AndroidAPS. Hvis du ikke er sikker på eller ikke ønsker at overskrive de basale indstillinger på din pumpe, tryk på annuller og gentag skift til pumpen på et senere tidspunkt.</string>
|
||||||
<string name="error_adding_treatment_title">Behandlingsdata ukomplette</string>
|
<string name="error_adding_treatment_title">Behandlingsdata ukomplette</string>
|
||||||
|
@ -751,6 +763,8 @@
|
||||||
<string name="profilenamecontainsdot">Profilnavn indeholder punktum.\nDette understøttes ikke af NS.\nProfilen er ikke uploadet til NS.</string>
|
<string name="profilenamecontainsdot">Profilnavn indeholder punktum.\nDette understøttes ikke af NS.\nProfilen er ikke uploadet til NS.</string>
|
||||||
<string name="low_mark_comment">Nedre værdi for målområde (kun visning)</string>
|
<string name="low_mark_comment">Nedre værdi for målområde (kun visning)</string>
|
||||||
<string name="high_mark_comment">Øvre værdi for målområde (kun visning)</string>
|
<string name="high_mark_comment">Øvre værdi for målområde (kun visning)</string>
|
||||||
|
<string name="age">Alder</string>
|
||||||
|
<string name="weight_label">Vægt</string>
|
||||||
<string name="id">ID:</string>
|
<string name="id">ID:</string>
|
||||||
<string name="submit">Send</string>
|
<string name="submit">Send</string>
|
||||||
<string name="mostcommonprofile">Mest almindelige profil:</string>
|
<string name="mostcommonprofile">Mest almindelige profil:</string>
|
||||||
|
@ -800,6 +814,7 @@
|
||||||
<string name="smscommunicator_otp_install_info">På hver follower telefon installeres Authenticator app, der understøtter RFC 6238 TOTP tokens. Populære gratis apps er:\n • Authy\n • Google Authenticator\n • LastPass Authenticator\n • FreeOTP Authenticator</string>
|
<string name="smscommunicator_otp_install_info">På hver follower telefon installeres Authenticator app, der understøtter RFC 6238 TOTP tokens. Populære gratis apps er:\n • Authy\n • Google Authenticator\n • LastPass Authenticator\n • FreeOTP Authenticator</string>
|
||||||
<string name="smscommunicator_otp_reset_warning">Ved at nulstille autentificering gør du alle allerede proviserede autentificatorer ugyldige. Du bliver nødt til at opsætte dem igen!</string>
|
<string name="smscommunicator_otp_reset_warning">Ved at nulstille autentificering gør du alle allerede proviserede autentificatorer ugyldige. Du bliver nødt til at opsætte dem igen!</string>
|
||||||
<string name="overview_show_predictions">Forudsigelser</string>
|
<string name="overview_show_predictions">Forudsigelser</string>
|
||||||
|
<string name="overview_show_treatments">Behandlinger</string>
|
||||||
<string name="overview_show_deviationslope">Afvigelses hældning</string>
|
<string name="overview_show_deviationslope">Afvigelses hældning</string>
|
||||||
<string name="authorizationfailed">Godkendelse mislykkedes</string>
|
<string name="authorizationfailed">Godkendelse mislykkedes</string>
|
||||||
<string name="overview_show_absinsulin">Absolut insulin</string>
|
<string name="overview_show_absinsulin">Absolut insulin</string>
|
||||||
|
@ -869,6 +884,8 @@
|
||||||
<string name="ns_receive_profile_switch_summary">Accepter profil skift indtastet gennem NS eller NSClient</string>
|
<string name="ns_receive_profile_switch_summary">Accepter profil skift indtastet gennem NS eller NSClient</string>
|
||||||
<string name="ns_receive_offline_event">Modtag APS offline begivenheder</string>
|
<string name="ns_receive_offline_event">Modtag APS offline begivenheder</string>
|
||||||
<string name="ns_receive_offline_event_summary">Accepter APS Offline begivenheder indtastet gennem NS eller NSClient</string>
|
<string name="ns_receive_offline_event_summary">Accepter APS Offline begivenheder indtastet gennem NS eller NSClient</string>
|
||||||
|
<string name="ns_receive_tbr_eb">Modtag TBR og EB</string>
|
||||||
|
<string name="ns_receive_tbr_eb_summary">Accepter TBR og EB indtastet gennem en anden instans</string>
|
||||||
<string name="ns_receive_insulin">Modtag insulin</string>
|
<string name="ns_receive_insulin">Modtag insulin</string>
|
||||||
<string name="ns_receive_insulin_summary">Accepter insulin via NS eller NSClient (det er ikke afgivet, kun beregnet til IOB)</string>
|
<string name="ns_receive_insulin_summary">Accepter insulin via NS eller NSClient (det er ikke afgivet, kun beregnet til IOB)</string>
|
||||||
<string name="ns_receive_carbs">Modtag kulhydrater</string>
|
<string name="ns_receive_carbs">Modtag kulhydrater</string>
|
||||||
|
@ -891,6 +908,7 @@
|
||||||
<string name="errors">Fejl</string>
|
<string name="errors">Fejl</string>
|
||||||
<string name="ns_sync_slow">Reducer upload hastighed</string>
|
<string name="ns_sync_slow">Reducer upload hastighed</string>
|
||||||
<string name="data_status">BG data status</string>
|
<string name="data_status">BG data status</string>
|
||||||
|
<string name="remove_bg_readings">Fjern BG aflæsninger</string>
|
||||||
<string name="statuslights_cannula_age">Indstik alder</string>
|
<string name="statuslights_cannula_age">Indstik alder</string>
|
||||||
<string name="statuslights_patch_pump_age">patch pumpe alder</string>
|
<string name="statuslights_patch_pump_age">patch pumpe alder</string>
|
||||||
<string name="patch_pump">Patch pumpe</string>
|
<string name="patch_pump">Patch pumpe</string>
|
||||||
|
@ -898,8 +916,110 @@
|
||||||
<string name="bg_too_close">BG for tæt:\n%1$s\n%2$s</string>
|
<string name="bg_too_close">BG for tæt:\n%1$s\n%2$s</string>
|
||||||
<string name="identification">Identifikation (e-mail, FB eller Discord alias osv.)</string>
|
<string name="identification">Identifikation (e-mail, FB eller Discord alias osv.)</string>
|
||||||
<string name="identification_not_set">Identifikation ikke indstillet i udvikler-tilstand</string>
|
<string name="identification_not_set">Identifikation ikke indstillet i udvikler-tilstand</string>
|
||||||
|
<string name="a11y_dialog">dialog</string>
|
||||||
|
<string name="a11y_current_bg">nuværende blodglukose</string>
|
||||||
|
<string name="a11_correction_percentage">korrekt resultat med %</string>
|
||||||
|
<string name="a11_correction_units">korrekt resultat med enheder</string>
|
||||||
<string name="not_available_full">Ikke tilgængelig</string>
|
<string name="not_available_full">Ikke tilgængelig</string>
|
||||||
|
<string name="a11y_high">høj</string>
|
||||||
|
<string name="a11y_inrange">inden for området</string>
|
||||||
|
<string name="a11y_low">lav</string>
|
||||||
|
<string name="a11y_arrow_double_down">falder hurtigt</string>
|
||||||
|
<string name="a11y_arrow_single_down">falder</string>
|
||||||
|
<string name="a11y_arrow_forty_five_down">falder langsomt</string>
|
||||||
|
<string name="a11y_arrow_flat">stabil</string>
|
||||||
|
<string name="a11y_arrow_forty_five_up">stiger langsomt</string>
|
||||||
|
<string name="a11y_arrow_single_up">stiger</string>
|
||||||
|
<string name="a11y_arrow_double_up">stiger hurtigt</string>
|
||||||
|
<string name="a11y_arrow_none">ingen</string>
|
||||||
|
<string name="a11y_arrow_unknown">ukendt</string>
|
||||||
|
<string name="a11y_graph">graf</string>
|
||||||
|
<string name="a11y_bg_quality">blodglukose kvalitet</string>
|
||||||
|
<string name="a11y_bg_quality_recalculated">genberegnet</string>
|
||||||
|
<string name="a11y_bg_quality_doubles">dobbelt postering</string>
|
||||||
|
<string name="a11y_insulin_label">insulin</string>
|
||||||
|
<string name="a11y_blood_glucose">blodglucose</string>
|
||||||
|
<string name="a11y_bg_outdated">forældet</string>
|
||||||
|
<string name="a11y_carb_reminder">Indstil påmindelse</string>
|
||||||
|
<string name="a11y_add_new_profile">tilføj ny profil</string>
|
||||||
|
<string name="a11y_clone_profile">klon nuværende profil</string>
|
||||||
|
<string name="a11y_delete_current_profile">slet nuværende profil</string>
|
||||||
|
<string name="a11y_add_new_to_list">tilføj ny til listen</string>
|
||||||
<!-- Theme switcher dark and light mode-->
|
<!-- Theme switcher dark and light mode-->
|
||||||
|
<string name="theme_switcher_summary">Vælg mørk, lys eller følg systemtemaet</string>
|
||||||
|
<string name="app_color_scheme">App Farvetema</string>
|
||||||
|
<string name="dark_theme">Mørkt tema</string>
|
||||||
|
<string name="light_theme">Lyst tema</string>
|
||||||
|
<string name="follow_system_theme">Brug enhedens tema</string>
|
||||||
<!-- WEAR OS-->
|
<!-- WEAR OS-->
|
||||||
|
<string name="wear_action_tempt_preset_error">Midlertidigmål ukendt forudindstilling: %1$s</string>
|
||||||
|
<string name="wear_action_tempt_cancel_message">Annullér aktuelt midlertidig mål?</string>
|
||||||
|
<string name="wear_action_tempt_unit_error">Forskellige enheder brugt på ur og telefon!</string>
|
||||||
|
<string name="wear_action_tempt_zero_message">0-mål - annuller midlertidigt mål?</string>
|
||||||
|
<string name="wear_action_tempt_min_bg_error">Min-BS udenfor området!</string>
|
||||||
|
<string name="wear_action_tempt_max_bg_error">Max-BS udenfor området!</string>
|
||||||
|
<string name="wear_action_tempt_manual_range_message">Midlertidigt mål:\nMin: %1$s\nMax: %2$s\nVarighed: %3$s</string>
|
||||||
|
<string name="wear_action_tempt_manual_message">Midlertigt mål:\nMål: %1$s\nVarighed: %2$s</string>
|
||||||
|
<string name="wear_action_tempt_preset_message">Midlertigt mål:\Grund: %1$s\nMål: %2$s\nVarighed: %3$s</string>
|
||||||
|
<string name="quick_wizard_message">Hurtigguide: %1$s\nInsulin: %2$.2fE\nKH: %3$dg</string>
|
||||||
|
<string name="wizard_result">Guide:\nInsulin: %1$.2fE\nKH: %2$dg</string>
|
||||||
|
<string name="overview_editquickwizard_show_on_device">Vis post på enhed:</string>
|
||||||
|
<string name="quick_wizard_not_available">Valgt guide er ikke længere tilgængeligt. Opdater venligst din widget</string>
|
||||||
|
<string name="wizard_no_actual_bg">Ingen nylig BG til at basere beregningen på!</string>
|
||||||
|
<string name="wizard_no_active_profile">Ingen aktiv profil angivet!</string>
|
||||||
|
<string name="wizard_no_cob">Ukendt COB! BG læsning mangler eller nylig app genstart?</string>
|
||||||
|
<string name="wizard_carbs_constraint">KH begrænsninger overtrådt!</string>
|
||||||
|
<string name="wizard_explain_calc">Calc (IC: %2$.1f, ISF: %2$.1f) fra:\"</string>
|
||||||
|
<string name="wizard_explain_carbs">Kulhydrater: %1$.2fE</string>
|
||||||
|
<string name="wizard_explain_cob">COB: %1$.0fg %2$.2fE</string>
|
||||||
|
<string name="wizard_explain_bg">BS: %1$.2fE</string>
|
||||||
|
<string name="wizard_explain_basal_iob">Basal IOB: %1$.2fE</string>
|
||||||
|
<string name="wizard_explain_bolus_iob">Bolus IOB: %1$.2fE</string>
|
||||||
|
<string name="wizard_explain_superbolus">Superbolus: %1$.2fE</string>
|
||||||
|
<string name="wizard_explain_trend">15\' trend: %1$.2fE</string>
|
||||||
|
<string name="wizard_explain_percent">Procent: %1$.2fE x %2$d%% ≈ %3$.2fE</string>
|
||||||
|
<string name="wizard_constraint_bolus_size">Overtrædelse af insulinbegrænsning!\nKan ikke levere %1$.2fE</string>
|
||||||
|
<string name="wizard_explain_tt">Midl: %1$s</string>
|
||||||
|
<string name="wizard_explain_tt_to">%1$s til %2$s</string>
|
||||||
|
<string name="wizard_pump_not_available">Ingen pumpe tilgængelig!</string>
|
||||||
|
<string name="wear_unknown_action_string">Ukendt kommando:</string>
|
||||||
|
<string name="overview_editquickwizard_percentage">Procentdel</string>
|
||||||
|
<string name="app_default">Program standard</string>
|
||||||
|
<string name="show_invalidated_records">Vis ugyldige / fjernede poster</string>
|
||||||
|
<string name="hide_invalidated_records">Skjul ugyldige / fjernede poster</string>
|
||||||
|
<string name="select_profile">Vælg profil, du vil redigere</string>
|
||||||
|
<string name="refresh_from_nightscout">Opdater fra Nightscout</string>
|
||||||
|
<string name="remove_selected_items">Fjern valgte elementer</string>
|
||||||
|
<string name="select_for_removal">Vælg for at fjerne</string>
|
||||||
|
<string name="profile_changes">Profil ændringer</string>
|
||||||
|
<string name="tempt_targets">Midlertidig mål </string>
|
||||||
|
<string name="carbs_and_bolus">Kulhydrater og bolus</string>
|
||||||
|
<string name="confirm_remove_multiple_items">Er du sikker på, at du vil fjerne %1$d elementer?</string>
|
||||||
|
<string name="no_records_available">Ingen poster tilgængelige</string>
|
||||||
|
<string name="hide_loop">Skjul loop</string>
|
||||||
|
<string name="show_loop">Vis loop</string>
|
||||||
|
<string name="count_selected">%1$d valgt</string>
|
||||||
|
<string name="sort_label">Sortér</string>
|
||||||
|
<string name="dialog_canceled">Dialog annulleret</string>
|
||||||
|
<string name="below" comment="below "in range"">Under</string>
|
||||||
|
<string name="in_range">Inden for området</string>
|
||||||
|
<string name="above" comment="above "in range"">Over</string>
|
||||||
|
<string name="show_loop_records">Vis loop poster</string>
|
||||||
|
<string name="show_hide_records">Skjul loop poster</string>
|
||||||
|
<string name="widget_description">AndroidAPS widget</string>
|
||||||
|
<string name="configure">Indstil gennemsigtighed</string>
|
||||||
|
<string name="loop_status">Loop status</string>
|
||||||
|
<string name="a11y_otp_qr_code">QR-kode til opsætning af engangs kodeord</string>
|
||||||
|
<string name="a11y_open_settings">Åbn indstillinger</string>
|
||||||
|
<string name="a11y_set_carb_timer">indstil KH alarm</string>
|
||||||
|
<string name="device_all">Alle</string>
|
||||||
|
<string name="device_phone">Telefon</string>
|
||||||
|
<string name="device_watch">Ur</string>
|
||||||
|
<string name="a11y_only_on_watch">kun på ur</string>
|
||||||
|
<string name="a11y_only_on_phone">kun på telefon</string>
|
||||||
|
<string name="a11y_drag_and_drop_handle">træk og slip håndtering</string>
|
||||||
<!-- Aidex Cgms -->
|
<!-- Aidex Cgms -->
|
||||||
|
<string name="aidex">GlucoRx Aidex</string>
|
||||||
|
<string name="aidex_short">Aidex</string>
|
||||||
|
<string name="description_source_aidex">Modtag BG-værdier fra GlucoRx Aidex CGMS.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<string name="codeaccepted">Código aceito</string>
|
<string name="codeaccepted">Código aceito</string>
|
||||||
<string name="codeinvalid">Código inválido</string>
|
<string name="codeinvalid">Código inválido</string>
|
||||||
<string name="objectives_exam_objective">Prove seu conhecimento</string>
|
<string name="objectives_exam_objective">Prove seu conhecimento</string>
|
||||||
<string name="objectives_exam_gate">Estude as perguntas. Haverá quatro respostas possíveis para cada pergunta. Pode ter mais do que uma resposta correta. Por favor, marque todas as que estão corretas e selecione VERIFY.</string>
|
<string name="objectives_exam_gate">Estude as perguntas. Haverá quatro respostas possíveis para cada pergunta. Pode ter mais do que uma resposta correta. Por favor, marque todas as que estão corretas e selecione VERIFICAR.</string>
|
||||||
<string name="answerdisabledto">Resposta desativada até: %1$s</string>
|
<string name="answerdisabledto">Resposta desativada até: %1$s</string>
|
||||||
<string name="wronganswer">Resposta errada!</string>
|
<string name="wronganswer">Resposta errada!</string>
|
||||||
<string name="unfinshed_button">Próximo inacabado</string>
|
<string name="unfinshed_button">Próximo inacabado</string>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<string name="usetemptarget_hint" formatted="false">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen</string>
|
<string name="usetemptarget_hint" formatted="false">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen</string>
|
||||||
<string name="useaction_hint" formatted="false">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#config-builder</string>
|
<string name="useaction_hint" formatted="false">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#config-builder</string>
|
||||||
<string name="usescale_hint" formatted="false">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen</string>
|
<string name="usescale_hint" formatted="false">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen</string>
|
||||||
<string name="notconnected">Não está ligado à internet</string>
|
<string name="notconnected">Não está conectado à internet</string>
|
||||||
<string name="failedretrievetime">Falha no tempo de recuperação</string>
|
<string name="failedretrievetime">Falha no tempo de recuperação</string>
|
||||||
<string name="requirementnotmet">Requisitos de objetivo não cumpridos</string>
|
<string name="requirementnotmet">Requisitos de objetivo não cumpridos</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -285,9 +285,9 @@
|
||||||
<string name="openapsama_autosens_adjusttargets">Autosens também ajusta os alvos</string>
|
<string name="openapsama_autosens_adjusttargets">Autosens também ajusta os alvos</string>
|
||||||
<string name="openapsama_autosens_adjusttargets_summary">Valor padrão: true\nÉ usado para permitir que autosens possa ajustar os valores alvo de glucose no sangue (BG), além de ISF e basais.</string>
|
<string name="openapsama_autosens_adjusttargets_summary">Valor padrão: true\nÉ usado para permitir que autosens possa ajustar os valores alvo de glucose no sangue (BG), além de ISF e basais.</string>
|
||||||
<string name="openapsama_bolussnooze_dia_divisor_summary">Valor padrão: 2\nBolus Snooze (pausa após bolus) é executado depois de realizar um bolus por refeição Desta maneira o algoritmo não irá contrariar com temporárias baixas logo depois da refeição. O valor padrão é 2; Então uma duração de ação da insulina (DIA) de 5h significa que o Bolus Snooze irá ser gradualmente reduzido ao longo de 2,5 horas = 5/2 = DIA/Valor padrão.</string>
|
<string name="openapsama_bolussnooze_dia_divisor_summary">Valor padrão: 2\nBolus Snooze (pausa após bolus) é executado depois de realizar um bolus por refeição Desta maneira o algoritmo não irá contrariar com temporárias baixas logo depois da refeição. O valor padrão é 2; Então uma duração de ação da insulina (DIA) de 5h significa que o Bolus Snooze irá ser gradualmente reduzido ao longo de 2,5 horas = 5/2 = DIA/Valor padrão.</string>
|
||||||
<string name="openapsama_min_5m_carbimpact_summary">Valor padrão: 3.0 para detecção avançada de refeições (AMA) ou 8.0 para super micro bolus (SMB). Esta é a configuração padrão para o calculo de quanto varia a cada 5 min a glucose no sangue (BG) devido à absorção de carboidratos. O padrão é 3mg/ dl / 5min. Isso afeta a rapidez com que decaem as calorias no corpo (COB), e quantos carboidratos terão de ser considerados no cálculo da previsão de BG, quando é que a BG está baixando mais do que o esperado ou não subindo como esperado.</string>
|
<string name="openapsama_min_5m_carbimpact_summary">Valor padrão: 3.0 para assitência avançada de refeições (AAR) ou 8.0 para super micro bolus (SMB). Esta é a configuração padrão para o calculo de quanto varia a cada 5 min a glicemia (BG) devido à absorção de carboidratos. O padrão é 3mg/dl/5min. Isso afeta a rapidez com que decaem os carboidratos ativos (CA) e quanta absorção de carboidrato será considerada no cálculo da previsão de glicemia futura, tornando possível notar que glicemia está baixando mais do que o esperado ou não subindo como esperado.</string>
|
||||||
<string name="openapsama_link_to_preferncejson_doc_txt">Atenção!\n Normalmente não é necessário modificar os valores abaixo. Por favor PRESSIONE AQUI e LEIA o texto para garantir que ENTENDE as consequenciais antes de alterar algum destes valores.</string>
|
<string name="openapsama_link_to_preferncejson_doc_txt">Atenção!\n Normalmente não é necessário modificar os valores abaixo. Por favor PRESSIONE AQUI e LEIA o texto para garantir que ENTENDE as consequências antes de alterar qualquer um destes valores.</string>
|
||||||
<string name="smscommunicator_invalidphonennumber">SMS número de telefone inválido</string>
|
<string name="smscommunicator_invalidphonennumber">Número de telefone inválido para comunicação por SMS</string>
|
||||||
<string name="overview_calibration">Calibração</string>
|
<string name="overview_calibration">Calibração</string>
|
||||||
<string name="xdripnotinstalled">xDrip+ não está instalado</string>
|
<string name="xdripnotinstalled">xDrip+ não está instalado</string>
|
||||||
<string name="calibrationsent">Calibração enviada para o xDrip+</string>
|
<string name="calibrationsent">Calibração enviada para o xDrip+</string>
|
||||||
|
@ -309,41 +309,41 @@
|
||||||
<string name="nsclientinternal_secret_dialogtitle">NS API secret</string>
|
<string name="nsclientinternal_secret_dialogtitle">NS API secret</string>
|
||||||
<string name="nsclientinternal_secret_dialogmessage">Insira NS API secret (min 12 caract.)</string>
|
<string name="nsclientinternal_secret_dialogmessage">Insira NS API secret (min 12 caract.)</string>
|
||||||
<string name="deliver_now">Entregar agora</string>
|
<string name="deliver_now">Entregar agora</string>
|
||||||
<string name="clear_queue">Limpar fila de espera</string>
|
<string name="clear_queue">Limpar fila</string>
|
||||||
<string name="show_queue">Mostrar fila</string>
|
<string name="show_queue">Mostrar fila</string>
|
||||||
<string name="queue">Fila:</string>
|
<string name="queue">Fila:</string>
|
||||||
<string name="status">Status:</string>
|
<string name="status">Status:</string>
|
||||||
<string name="clearlog">Limpar Log</string>
|
<string name="clearlog">Limpar registros</string>
|
||||||
<string name="nowritepermission">NSCLIENT não tem permissão de escrita. Senha da API errada?</string>
|
<string name="nowritepermission">NSCLIENT não tem permissão de escrita. Senha da API errada?</string>
|
||||||
<string name="wear_settings">Definições Wear</string>
|
<string name="wear_settings">Definições Wear</string>
|
||||||
<string name="wear_detailedIOB_title">Mostrar IOB detalhado</string>
|
<string name="wear_detailedIOB_title">Mostrar detalhes da IA</string>
|
||||||
<string name="wear_detailedIOB_summary">Dividir IOB entre IOB de bolus e de basal na face do relógio</string>
|
<string name="wear_detailedIOB_summary">Dividir IA entre IA de bolus e de basal na face do relógio</string>
|
||||||
<string name="nosuccess">não foi bem sucedido - por favor, verifique o telefone</string>
|
<string name="nosuccess">não foi bem sucedido - por favor, verifique o telefone</string>
|
||||||
<string name="notavailable">n/a</string>
|
<string name="notavailable">n/a</string>
|
||||||
<string name="patientage">Tipo de paciente</string>
|
<string name="patientage">Tipo de paciente</string>
|
||||||
<string name="child">Criança</string>
|
<string name="child">Criança</string>
|
||||||
<string name="teenage">Adolescente</string>
|
<string name="teenage">Adolescente</string>
|
||||||
<string name="adult">Adulto</string>
|
<string name="adult">Adulto</string>
|
||||||
<string name="resistantadult">Adulto resistente insulina</string>
|
<string name="resistantadult">Adulto resistente à insulina</string>
|
||||||
<string name="pregnant">Grávida</string>
|
<string name="pregnant">Grávida</string>
|
||||||
<string name="patientage_summary">Selecione o tipo de paciente para configurar os limites de segurança</string>
|
<string name="patientage_summary">Selecione o tipo de paciente para configurar os limites de segurança</string>
|
||||||
<string name="patient_name">Nome do Paciente</string>
|
<string name="patient_name">Nome do Paciente</string>
|
||||||
<string name="patient_name_summary">Por favor, forneça nome do paciente ou apelido para diferenciar entre várias configurações</string>
|
<string name="patient_name_summary">Por favor, forneça nome do paciente ou apelido para diferenciar entre várias configurações</string>
|
||||||
<string name="patient_name_default" comment="This is default patient display name, when user does not provide real one">Usuário</string>
|
<string name="patient_name_default" comment="This is default patient display name, when user does not provide real one">Usuário</string>
|
||||||
<string name="Glimp">Glimp</string>
|
<string name="Glimp">Glimp</string>
|
||||||
<string name="needwhitelisting">%1$s necessita de autorizar a não optimização da bateria para assegurar a performance necessária</string>
|
<string name="needwhitelisting">%1$s necessita autorização de execução sem otimização da bateria para assegurar a performance necessária</string>
|
||||||
<string name="loopsuspended">Loop suspenso</string>
|
<string name="loopsuspended">Loop suspenso</string>
|
||||||
<string name="loopsuspendedfor">Suspendido (%1$d m)</string>
|
<string name="loopsuspendedfor">Suspenso (%1$d m)</string>
|
||||||
<string name="suspendloopfor1h">Suspender loop por 1h</string>
|
<string name="suspendloopfor1h">Suspender loop por 1h</string>
|
||||||
<string name="suspendloopfor2h">Suspender loop por 2h</string>
|
<string name="suspendloopfor2h">Suspender loop por 2h</string>
|
||||||
<string name="suspendloopfor3h">Suspender loop por 3h</string>
|
<string name="suspendloopfor3h">Suspender loop por 3h</string>
|
||||||
<string name="suspendloopfor10h">Suspender loop por 10h</string>
|
<string name="suspendloopfor10h">Suspender loop por 10h</string>
|
||||||
<string name="disconnectpump">Bomba Desconectada</string>
|
<string name="disconnectpump">Bomba Desconectada</string>
|
||||||
<string name="disconnectpumpfor15m">Desligar bomba por 15 min</string>
|
<string name="disconnectpumpfor15m">Desconectar bomba por 15 min</string>
|
||||||
<string name="disconnectpumpfor30m">Desligar bomba por 30 min</string>
|
<string name="disconnectpumpfor30m">Desconectar bomba por 30 min</string>
|
||||||
<string name="disconnectpumpfor1h">Desligar bomba por 1 h</string>
|
<string name="disconnectpumpfor1h">Desconectar bomba por 1 h</string>
|
||||||
<string name="disconnectpumpfor2h">Desligar bomba por 2 h</string>
|
<string name="disconnectpumpfor2h">Desconectar bomba por 2 h</string>
|
||||||
<string name="disconnectpumpfor3h">Desligar bomba por 3 h</string>
|
<string name="disconnectpumpfor3h">Desconectar bomba por 3 h</string>
|
||||||
<string name="duration15m">15 min</string>
|
<string name="duration15m">15 min</string>
|
||||||
<string name="duration30m">30 min</string>
|
<string name="duration30m">30 min</string>
|
||||||
<string name="duration1h">1 hora</string>
|
<string name="duration1h">1 hora</string>
|
||||||
|
@ -356,15 +356,15 @@
|
||||||
<string name="smscommunicator_loopsuspended">Loop suspenso</string>
|
<string name="smscommunicator_loopsuspended">Loop suspenso</string>
|
||||||
<string name="smscommunicator_loopresumed">Loop retomado</string>
|
<string name="smscommunicator_loopresumed">Loop retomado</string>
|
||||||
<string name="bg_trend_label">Tendência 15 min</string>
|
<string name="bg_trend_label">Tendência 15 min</string>
|
||||||
<string name="treatments_wizard_cob_label">COB</string>
|
<string name="treatments_wizard_cob_label">CA</string>
|
||||||
<string name="superbolus">Superbólus</string>
|
<string name="superbolus">Superbolus</string>
|
||||||
<string name="ns_logappstartedevent">Registar inicio da app no NS</string>
|
<string name="ns_logappstartedevent">Registrar início do app no NS</string>
|
||||||
<string name="restartingapp">A sair da aplicação para aplicar as configurações.</string>
|
<string name="restartingapp">Saindo do app para aplicar as configurações.</string>
|
||||||
<string name="configbuilder_insulin_description">Qual o tipo de insulina que está a utilizar?</string>
|
<string name="configbuilder_insulin_description">Que tipo de insulina está usando?</string>
|
||||||
<string name="fastactinginsulincomment">Novorapid, Novolog, Humalog</string>
|
<string name="fastactinginsulincomment">Novorapid, Novolog, Humalog</string>
|
||||||
<string name="ultrafastactinginsulincomment">Fiasp</string>
|
<string name="ultrafastactinginsulincomment">Fiasp</string>
|
||||||
<string name="insulin_shortname">INS</string>
|
<string name="insulin_shortname">INS</string>
|
||||||
<string name="enablesuperbolus">Ativar superbólus no assistente</string>
|
<string name="enablesuperbolus">Ativar superbolus no assistente</string>
|
||||||
<string name="enablesuperbolus_summary">Habilite a funcionalidade de superbolus no assistente. Não habilite até que aprenda o funcionamento. PODE CAUSAR OVERDOSE DE INSULINA SE USAR INDISCRIMINADAMENTE!</string>
|
<string name="enablesuperbolus_summary">Habilite a funcionalidade de superbolus no assistente. Não habilite até que aprenda o funcionamento. PODE CAUSAR OVERDOSE DE INSULINA SE USAR INDISCRIMINADAMENTE!</string>
|
||||||
<string name="show_statuslights">Mostrar luzes de estado no ecrã principal</string>
|
<string name="show_statuslights">Mostrar luzes de estado no ecrã principal</string>
|
||||||
<string name="statuslights_cage_warning">Aviso de limite da vida útil da cânula [h]</string>
|
<string name="statuslights_cage_warning">Aviso de limite da vida útil da cânula [h]</string>
|
||||||
|
@ -377,14 +377,14 @@
|
||||||
<string name="statuslights_sbat_critical">Aviso de limite crítico do nível da bateria do sensor [%]</string>
|
<string name="statuslights_sbat_critical">Aviso de limite crítico do nível da bateria do sensor [%]</string>
|
||||||
<string name="statuslights_bage_warning">Aviso de limite da vida útil da bateria da bomba [h]</string>
|
<string name="statuslights_bage_warning">Aviso de limite da vida útil da bateria da bomba [h]</string>
|
||||||
<string name="statuslights_bage_critical">Aviso de limite crítico da vida útil da bateria da bomba [h]</string>
|
<string name="statuslights_bage_critical">Aviso de limite crítico da vida útil da bateria da bomba [h]</string>
|
||||||
<string name="statuslights_res_warning">Limite de aviso de nível de reservatório [U]</string>
|
<string name="statuslights_res_warning">Aviso de limite de nível de reservatório [U]</string>
|
||||||
<string name="statuslights_res_critical">Limite crítico de nível de reservatório [U]</string>
|
<string name="statuslights_res_critical">Aviso de limite crítico de nível de reservatório [U]</string>
|
||||||
<string name="activity_shortname">ACT</string>
|
<string name="activity_shortname">ACT</string>
|
||||||
<string name="nav_about">Sobre</string>
|
<string name="nav_about">Sobre</string>
|
||||||
<string name="smscommunicator_missingsmspermission">Falta de permissão SMS</string>
|
<string name="smscommunicator_missingsmspermission">Falta de permissão SMS</string>
|
||||||
<string name="smscommunicator_missingphonestatepermission">Falta permissão do estado do telefone</string>
|
<string name="smscommunicator_missingphonestatepermission">Falta permissão do estado do telefone</string>
|
||||||
<string name="xdripstatus_shortname">xds</string>
|
<string name="xdripstatus_shortname">xds</string>
|
||||||
<string name="wear_showbgi_title">Mostrar BGI</string>
|
<string name="wear_showbgi_title">Mostrar IG (Impacto na Glicemia)</string>
|
||||||
<string name="wear_showbgi_summary">Adicionar BGI à linha de status</string>
|
<string name="wear_showbgi_summary">Adicionar BGI à linha de status</string>
|
||||||
<string name="overview_extendedbolus_cancel_button">Cancelar Bólus Estendido</string>
|
<string name="overview_extendedbolus_cancel_button">Cancelar Bólus Estendido</string>
|
||||||
<string name="doprofileswitch">Fazer Mudança De Perfil</string>
|
<string name="doprofileswitch">Fazer Mudança De Perfil</string>
|
||||||
|
|
|
@ -442,6 +442,7 @@
|
||||||
<string name="enableuam_summary">Detekcia neoznámených jedál</string>
|
<string name="enableuam_summary">Detekcia neoznámených jedál</string>
|
||||||
<string name="insulin_oref_peak">Čas vrcholu IOB krivky</string>
|
<string name="insulin_oref_peak">Čas vrcholu IOB krivky</string>
|
||||||
<string name="insulin_peak_time">Vrchol krivky [min]</string>
|
<string name="insulin_peak_time">Vrchol krivky [min]</string>
|
||||||
|
<string name="insulin_peak">Vrchol</string>
|
||||||
<string name="free_peak_oref">Voliteľný vrchol - Oref</string>
|
<string name="free_peak_oref">Voliteľný vrchol - Oref</string>
|
||||||
<string name="rapid_acting_oref">Rýchlo pôsobiaci - Oref</string>
|
<string name="rapid_acting_oref">Rýchlo pôsobiaci - Oref</string>
|
||||||
<string name="ultrarapid_oref">Ultra rýchly - Oref</string>
|
<string name="ultrarapid_oref">Ultra rýchly - Oref</string>
|
||||||
|
|
|
@ -533,6 +533,7 @@
|
||||||
<string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string>
|
<string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string>
|
||||||
<string name="insulin_oref_peak">IOB Curve Peak Time</string>
|
<string name="insulin_oref_peak">IOB Curve Peak Time</string>
|
||||||
<string name="insulin_peak_time">Peak Time [min]</string>
|
<string name="insulin_peak_time">Peak Time [min]</string>
|
||||||
|
<string name="insulin_peak">Peak</string>
|
||||||
<string name="free_peak_oref">Free-Peak Oref</string>
|
<string name="free_peak_oref">Free-Peak Oref</string>
|
||||||
<string name="rapid_acting_oref">Rapid-Acting Oref</string>
|
<string name="rapid_acting_oref">Rapid-Acting Oref</string>
|
||||||
<string name="ultrarapid_oref">Ultra-Rapid Oref</string>
|
<string name="ultrarapid_oref">Ultra-Rapid Oref</string>
|
||||||
|
|
46
app/src/main/res/xml/pref_autotune.xml
Normal file
46
app/src/main/res/xml/pref_autotune.xml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="@string/key_autotune_plugin"
|
||||||
|
android:title="@string/autotune_settings"
|
||||||
|
app:initialExpandedChildrenCount="10">
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/key_autotune_auto"
|
||||||
|
android:summary="@string/autotune_auto_summary"
|
||||||
|
android:title="@string/autotune_auto_title" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/key_autotune_categorize_uam_as_basal"
|
||||||
|
android:summary="@string/autotune_categorize_uam_as_basal_summary"
|
||||||
|
android:title="@string/autotune_categorize_uam_as_basal_title" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/key_autotune_tune_insulin_curve"
|
||||||
|
android:summary="@string/autotune_tune_insulin_curve_summary"
|
||||||
|
android:title="@string/autotune_tune_insulin_curve_title" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:defaultValue="5"
|
||||||
|
android:inputType="number"
|
||||||
|
android:key="@string/key_autotune_default_tune_days"
|
||||||
|
android:summary="@string/autotune_default_tune_days_summary"
|
||||||
|
android:title="@string/autotune_default_tune_days_title" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/key_autotune_circadian_ic_isf"
|
||||||
|
android:summary="@string/autotune_circadian_ic_isf_summary"
|
||||||
|
android:title="@string/autotune_circadian_ic_isf_title" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/key_autotune_additional_log"
|
||||||
|
android:summary="@string/autotune_additional_log_summary"
|
||||||
|
android:title="@string/autotune_additional_log_title" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
</androidx.preference.PreferenceScreen>
|
|
@ -42,6 +42,7 @@ abstract class AutomationModule {
|
||||||
@ContributesAndroidInjector abstract fun actionCarePortalEventInjector(): ActionCarePortalEvent
|
@ContributesAndroidInjector abstract fun actionCarePortalEventInjector(): ActionCarePortalEvent
|
||||||
@ContributesAndroidInjector abstract fun actionProfileSwitchInjector(): ActionProfileSwitch
|
@ContributesAndroidInjector abstract fun actionProfileSwitchInjector(): ActionProfileSwitch
|
||||||
@ContributesAndroidInjector abstract fun actionProfileSwitchPercentInjector(): ActionProfileSwitchPercent
|
@ContributesAndroidInjector abstract fun actionProfileSwitchPercentInjector(): ActionProfileSwitchPercent
|
||||||
|
@ContributesAndroidInjector abstract fun actionRunAutotuneInjector(): ActionRunAutotune
|
||||||
@ContributesAndroidInjector abstract fun actionSendSMSInjector(): ActionSendSMS
|
@ContributesAndroidInjector abstract fun actionSendSMSInjector(): ActionSendSMS
|
||||||
@ContributesAndroidInjector abstract fun actionStartTempTargetInjector(): ActionStartTempTarget
|
@ContributesAndroidInjector abstract fun actionStartTempTargetInjector(): ActionStartTempTarget
|
||||||
@ContributesAndroidInjector abstract fun actionStopTempTargetInjector(): ActionStopTempTarget
|
@ContributesAndroidInjector abstract fun actionStopTempTargetInjector(): ActionStopTempTarget
|
||||||
|
|
|
@ -332,6 +332,7 @@ class AutomationPlugin @Inject constructor(
|
||||||
ActionCarePortalEvent(injector),
|
ActionCarePortalEvent(injector),
|
||||||
ActionProfileSwitchPercent(injector),
|
ActionProfileSwitchPercent(injector),
|
||||||
ActionProfileSwitch(injector),
|
ActionProfileSwitch(injector),
|
||||||
|
ActionRunAutotune(injector),
|
||||||
ActionSendSMS(injector)
|
ActionSendSMS(injector)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,8 @@ abstract class Action(val injector: HasAndroidInjector) {
|
||||||
ActionProfileSwitch::class.java.simpleName -> ActionProfileSwitch(injector).fromJSON(data.toString())
|
ActionProfileSwitch::class.java.simpleName -> ActionProfileSwitch(injector).fromJSON(data.toString())
|
||||||
ActionProfileSwitchPercent::class.java.name,
|
ActionProfileSwitchPercent::class.java.name,
|
||||||
ActionProfileSwitchPercent::class.java.simpleName -> ActionProfileSwitchPercent(injector).fromJSON(data.toString())
|
ActionProfileSwitchPercent::class.java.simpleName -> ActionProfileSwitchPercent(injector).fromJSON(data.toString())
|
||||||
|
ActionRunAutotune::class.java.name,
|
||||||
|
ActionRunAutotune::class.java.simpleName -> ActionRunAutotune(injector).fromJSON(data.toString())
|
||||||
ActionSendSMS::class.java.name,
|
ActionSendSMS::class.java.name,
|
||||||
ActionSendSMS::class.java.simpleName -> ActionSendSMS(injector).fromJSON(data.toString())
|
ActionSendSMS::class.java.simpleName -> ActionSendSMS(injector).fromJSON(data.toString())
|
||||||
ActionStartTempTarget::class.java.name,
|
ActionStartTempTarget::class.java.name,
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions
|
||||||
|
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.androidaps.automation.R
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult
|
||||||
|
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||||
|
import info.nightscout.androidaps.interfaces.Autotune
|
||||||
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputProfileName
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder
|
||||||
|
import info.nightscout.androidaps.queue.Callback
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper
|
||||||
|
import info.nightscout.shared.logging.LTag
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import org.json.JSONObject
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ActionRunAutotune(injector: HasAndroidInjector) : Action(injector) {
|
||||||
|
@Inject lateinit var resourceHelper: ResourceHelper
|
||||||
|
@Inject lateinit var autotunePlugin: Autotune
|
||||||
|
@Inject lateinit var profileFunction: ProfileFunction
|
||||||
|
@Inject lateinit var activePlugin: ActivePlugin
|
||||||
|
@Inject lateinit var sp: SP
|
||||||
|
@Inject lateinit var uel: UserEntryLogger
|
||||||
|
|
||||||
|
var defaultValue = 0
|
||||||
|
private var inputProfileName = InputProfileName(rh, activePlugin, "", true)
|
||||||
|
private var daysBack = InputDuration(0, InputDuration.TimeUnit.DAYS)
|
||||||
|
|
||||||
|
override fun friendlyName(): Int = R.string.autotune_run
|
||||||
|
override fun shortDescription(): String = resourceHelper.gs(R.string.autotune_profile_name, inputProfileName.value)
|
||||||
|
@DrawableRes override fun icon(): Int = R.drawable.ic_actions_profileswitch
|
||||||
|
|
||||||
|
override fun doAction(callback: Callback) {
|
||||||
|
val autoSwitch = sp.getBoolean(R.string.key_autotune_auto, false)
|
||||||
|
val profileName = if (inputProfileName.value == rh.gs(R.string.active)) "" else inputProfileName.value
|
||||||
|
var message = if (autoSwitch) R.string.autotune_run_with_autoswitch else R.string.autotune_run_without_autoswitch
|
||||||
|
Thread {
|
||||||
|
autotunePlugin.atLog("[Automation] Run Autotune $profileName, ${daysBack.value} days, Autoswitch $autoSwitch")
|
||||||
|
autotunePlugin.aapsAutotune(daysBack.value, autoSwitch, profileName)
|
||||||
|
if (!autotunePlugin.lastRunSuccess) {
|
||||||
|
message = R.string.autotune_run_with_error
|
||||||
|
aapsLogger.error(LTag.AUTOMATION, "Error during Autotune Run")
|
||||||
|
}
|
||||||
|
callback.result(PumpEnactResult(injector).success(autotunePlugin.lastRunSuccess).comment(message))?.run()
|
||||||
|
}.start()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generateDialog(root: LinearLayout) {
|
||||||
|
if (defaultValue == 0)
|
||||||
|
defaultValue = sp.getInt(R.string.key_autotune_default_tune_days, 5)
|
||||||
|
daysBack.value = defaultValue
|
||||||
|
LayoutBuilder()
|
||||||
|
.add(LabelWithElement(rh, rh.gs(R.string.autotune_select_profile), "", inputProfileName))
|
||||||
|
.add(LabelWithElement(rh, rh.gs(R.string.autotune_tune_days), "", daysBack))
|
||||||
|
.build(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasDialog(): Boolean = true
|
||||||
|
|
||||||
|
override fun toJSON(): String {
|
||||||
|
val data = JSONObject()
|
||||||
|
.put("profileToTune", inputProfileName.value)
|
||||||
|
.put("tunedays", daysBack.value)
|
||||||
|
return JSONObject()
|
||||||
|
.put("type", this.javaClass.name)
|
||||||
|
.put("data", data)
|
||||||
|
.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromJSON(data: String): Action {
|
||||||
|
val o = JSONObject(data)
|
||||||
|
inputProfileName.value = JsonHelper.safeGetString(o, "profileToTune", "")
|
||||||
|
defaultValue = JsonHelper.safeGetInt(o, "tunedays")
|
||||||
|
if (defaultValue == 0)
|
||||||
|
defaultValue = sp.getInt(R.string.key_autotune_default_tune_days, 5)
|
||||||
|
daysBack.value = defaultValue
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isValid(): Boolean = profileFunction.getProfile() != null && activePlugin.getSpecificPluginsListByInterface(Autotune::class.java).first().isEnabled()
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ class InputDuration(
|
||||||
) : Element() {
|
) : Element() {
|
||||||
|
|
||||||
enum class TimeUnit {
|
enum class TimeUnit {
|
||||||
MINUTES, HOURS
|
MINUTES, HOURS, DAYS
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addToLayout(root: LinearLayout) {
|
override fun addToLayout(root: LinearLayout) {
|
||||||
|
@ -21,6 +21,9 @@ class InputDuration(
|
||||||
if (unit == TimeUnit.MINUTES) {
|
if (unit == TimeUnit.MINUTES) {
|
||||||
numberPicker = MinutesNumberPicker(root.context, null)
|
numberPicker = MinutesNumberPicker(root.context, null)
|
||||||
numberPicker.setParams(value.toDouble(), 5.0, 24 * 60.0, 10.0, DecimalFormat("0"), false, root.findViewById(R.id.ok))
|
numberPicker.setParams(value.toDouble(), 5.0, 24 * 60.0, 10.0, DecimalFormat("0"), false, root.findViewById(R.id.ok))
|
||||||
|
} else if (unit == TimeUnit.DAYS) {
|
||||||
|
numberPicker = MinutesNumberPicker(root.context, null)
|
||||||
|
numberPicker.setParams(value.toDouble(), 1.0, 30.0, 1.0, DecimalFormat("0"), false, root.findViewById(R.id.ok))
|
||||||
} else {
|
} else {
|
||||||
numberPicker = NumberPicker(root.context, null)
|
numberPicker = NumberPicker(root.context, null)
|
||||||
numberPicker.setParams(value.toDouble(), 1.0, 24.0, 1.0, DecimalFormat("0"), false, root.findViewById(R.id.ok))
|
numberPicker.setParams(value.toDouble(), 1.0, 24.0, 1.0, DecimalFormat("0"), false, root.findViewById(R.id.ok))
|
||||||
|
|
|
@ -25,7 +25,7 @@ class InputPercent() : Element() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val MIN = 70.0
|
const val MIN = 50.0
|
||||||
const val MAX = 130.0
|
const val MAX = 130.0
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,14 +10,15 @@ import info.nightscout.androidaps.automation.R
|
||||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||||
import info.nightscout.androidaps.interfaces.ResourceHelper
|
import info.nightscout.androidaps.interfaces.ResourceHelper
|
||||||
|
|
||||||
class InputProfileName(private val rh: ResourceHelper, private val activePlugin: ActivePlugin, val name: String = "") : Element() {
|
class InputProfileName(private val rh: ResourceHelper, private val activePlugin: ActivePlugin, val name: String = "", val addActive: Boolean = false) : Element() {
|
||||||
|
|
||||||
var value: String = name
|
var value: String = name
|
||||||
|
|
||||||
override fun addToLayout(root: LinearLayout) {
|
override fun addToLayout(root: LinearLayout) {
|
||||||
val profileStore = activePlugin.activeProfileSource.profile ?: return
|
val profileStore = activePlugin.activeProfileSource.profile ?: return
|
||||||
val profileList = profileStore.getProfileList()
|
val profileList = profileStore.getProfileList()
|
||||||
|
if (addActive)
|
||||||
|
profileList.add(0, rh.gs(R.string.active))
|
||||||
root.addView(
|
root.addView(
|
||||||
Spinner(root.context).apply {
|
Spinner(root.context).apply {
|
||||||
adapter = ArrayAdapter(root.context, R.layout.spinner_centered, profileList).apply {
|
adapter = ArrayAdapter(root.context, R.layout.spinner_centered, profileList).apply {
|
||||||
|
|
|
@ -120,4 +120,7 @@
|
||||||
<string name="confirm_remove_multiple_items">Opravdu chcete odstranit %1$d položek</string>
|
<string name="confirm_remove_multiple_items">Opravdu chcete odstranit %1$d položek</string>
|
||||||
<string name="sort_label">Seřadit</string>
|
<string name="sort_label">Seřadit</string>
|
||||||
<string name="system_automation">Automatizace systému</string>
|
<string name="system_automation">Automatizace systému</string>
|
||||||
|
<string name="run_automations">Spustit automatizace</string>
|
||||||
|
<string name="add_automation">Přidat pravidlo</string>
|
||||||
|
<string name="remove_sort">Odstranit/řadit</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
<string name="wifissidcompared">WiFi SSID %1$s %2$s</string>
|
<string name="wifissidcompared">WiFi SSID %1$s %2$s</string>
|
||||||
<string name="autosenscompared">Autosens %1$s %2$s %%</string>
|
<string name="autosenscompared">Autosens %1$s %2$s %%</string>
|
||||||
<string name="autosenslabel">Autosens %</string>
|
<string name="autosenslabel">Autosens %</string>
|
||||||
|
<string name="a11y_autosenslabel">Auto sens</string>
|
||||||
<string name="deltacompared">%3$s %1$s %2$s</string>
|
<string name="deltacompared">%3$s %1$s %2$s</string>
|
||||||
<string name="deltalabel">BS forskel</string>
|
<string name="deltalabel">BS forskel</string>
|
||||||
<string name="deltalabel_u">BS difference [%1$s]</string>
|
<string name="deltalabel_u">BS difference [%1$s]</string>
|
||||||
|
@ -112,4 +113,14 @@
|
||||||
<string name="automation_event">Automatiserings event</string>
|
<string name="automation_event">Automatiserings event</string>
|
||||||
<string name="reorder_label">Omorganisering</string>
|
<string name="reorder_label">Omorganisering</string>
|
||||||
<string name="user_action">Brugerhandling</string>
|
<string name="user_action">Brugerhandling</string>
|
||||||
|
<string name="remove_automation">Fjern automatisering</string>
|
||||||
|
<string name="sort_automation">Sortér automatisering</string>
|
||||||
|
<string name="remove_selected_items">Fjern valgte emner</string>
|
||||||
|
<string name="count_selected">%1$d valgt</string>
|
||||||
|
<string name="confirm_remove_multiple_items">Er du sikker på, at du vil fjerne %1$d elementer?</string>
|
||||||
|
<string name="sort_label">Sortér</string>
|
||||||
|
<string name="system_automation">System automatisering</string>
|
||||||
|
<string name="run_automations">Kør automatisering</string>
|
||||||
|
<string name="add_automation">Tilføj regel</string>
|
||||||
|
<string name="remove_sort">Fjern/sorter</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -120,4 +120,6 @@
|
||||||
<string name="confirm_remove_multiple_items">Êtes-vous sûr de vouloir supprimer %1$d entrée(s)</string>
|
<string name="confirm_remove_multiple_items">Êtes-vous sûr de vouloir supprimer %1$d entrée(s)</string>
|
||||||
<string name="sort_label">Trier</string>
|
<string name="sort_label">Trier</string>
|
||||||
<string name="system_automation">Système d\'automatisation</string>
|
<string name="system_automation">Système d\'automatisation</string>
|
||||||
|
<string name="add_automation">Ajouter une règle</string>
|
||||||
|
<string name="remove_sort">Supprimer/trier</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -9,7 +9,7 @@ buildscript {
|
||||||
rxkotlin_version = '3.0.1'
|
rxkotlin_version = '3.0.1'
|
||||||
room_version = '2.4.2'
|
room_version = '2.4.2'
|
||||||
lifecycle_version = '2.4.1'
|
lifecycle_version = '2.4.1'
|
||||||
dagger_version = '2.41'
|
dagger_version = '2.42'
|
||||||
coroutines_version = '1.6.1'
|
coroutines_version = '1.6.1'
|
||||||
activity_version = '1.3.1'
|
activity_version = '1.3.1'
|
||||||
fragmentktx_version = '1.3.6'
|
fragmentktx_version = '1.3.6'
|
||||||
|
@ -43,7 +43,7 @@ buildscript {
|
||||||
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
|
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.1.3'
|
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||||
classpath 'com.google.gms:google-services:4.3.10'
|
classpath 'com.google.gms:google-services:4.3.10'
|
||||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
||||||
|
|
||||||
|
|
|
@ -51,4 +51,10 @@
|
||||||
<string name="combo_tbr_count">TBR antal</string>
|
<string name="combo_tbr_count">TBR antal</string>
|
||||||
<string name="bolusstopped">Bolus stoppet</string>
|
<string name="bolusstopped">Bolus stoppet</string>
|
||||||
<string name="bolusstopping">Stopper bolus</string>
|
<string name="bolusstopping">Stopper bolus</string>
|
||||||
|
<string name="pump_commerror_label">Komm. Fejlantal</string>
|
||||||
|
<string name="show_comm_error_count_title">Vis komm. fejltælling</string>
|
||||||
|
<string name="show_comm_error_count_summary">Viser antal fejl, når du kommunikerer med Ruffy. I de fleste tilfælde betyder tal højere end 0 at Ruffy oplever kommunikationsproblemer (genstart kan være nødvendigt).</string>
|
||||||
|
<string name="combo_error_display_never">Aldrig</string>
|
||||||
|
<string name="combo_error_display_error">Ved Fejl</string>
|
||||||
|
<string name="combo_error_display_always">Altid</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package info.nightscout.androidaps.data
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.database.entities.Bolus
|
||||||
|
import kotlin.math.exp
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
class LocalInsulin constructor(val name:String?, val peak:Int = DEFAULT_PEAK, private val userDefinedDia: Double = DEFAULT_DIA) {
|
||||||
|
val dia
|
||||||
|
get(): Double {
|
||||||
|
val dia = userDefinedDia
|
||||||
|
return if (dia >= MIN_DIA) {
|
||||||
|
dia
|
||||||
|
} else {
|
||||||
|
MIN_DIA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val duration
|
||||||
|
get() = (60 * 60 * 1000L * dia).toLong()
|
||||||
|
|
||||||
|
fun iobCalcForTreatment(bolus: Bolus, time: Long): Iob {
|
||||||
|
val result = Iob()
|
||||||
|
if (bolus.amount != 0.0) {
|
||||||
|
val bolusTime = bolus.timestamp
|
||||||
|
val t = (time - bolusTime) / 1000.0 / 60.0
|
||||||
|
val td = dia * 60 //getDIA() always >= MIN_DIA
|
||||||
|
val tp = peak.toDouble()
|
||||||
|
// force the IOB to 0 if over DIA hours have passed
|
||||||
|
if (t < td) {
|
||||||
|
val tau = tp * (1 - tp / td) / (1 - 2 * tp / td)
|
||||||
|
val a = 2 * tau / td
|
||||||
|
val S = 1 / (1 - a + (1 + a) * exp(-td / tau))
|
||||||
|
result.activityContrib = bolus.amount * (S / tau.pow(2.0)) * t * (1 - t / td) * exp(-t / tau)
|
||||||
|
result.iobContrib = bolus.amount * (1 - S * (1 - a) * ((t.pow(2.0) / (tau * td * (1 - a)) - t / tau - 1) * Math.exp(-t / tau) + 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MIN_DIA = 5.0
|
||||||
|
private const val DEFAULT_DIA = 6.0
|
||||||
|
private const val DEFAULT_PEAK = 75
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package info.nightscout.androidaps.extensions
|
package info.nightscout.androidaps.extensions
|
||||||
|
|
||||||
import info.nightscout.androidaps.data.Iob
|
import info.nightscout.androidaps.data.Iob
|
||||||
|
import info.nightscout.androidaps.data.LocalInsulin
|
||||||
import info.nightscout.androidaps.database.embedments.InterfaceIDs
|
import info.nightscout.androidaps.database.embedments.InterfaceIDs
|
||||||
import info.nightscout.androidaps.database.entities.Bolus
|
import info.nightscout.androidaps.database.entities.Bolus
|
||||||
import info.nightscout.androidaps.database.entities.TherapyEvent
|
import info.nightscout.androidaps.database.entities.TherapyEvent
|
||||||
|
@ -16,6 +17,12 @@ fun Bolus.iobCalc(activePlugin: ActivePlugin, time: Long, dia: Double): Iob {
|
||||||
return insulinInterface.iobCalcForTreatment(this, time, dia)
|
return insulinInterface.iobCalcForTreatment(this, time, dia)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add specific calculation for Autotune (reference localInsulin for Peak/dia)
|
||||||
|
fun Bolus.iobCalc(time: Long, localInsulin: LocalInsulin): Iob {
|
||||||
|
if (!isValid || type == Bolus.Type.PRIMING ) return Iob()
|
||||||
|
return localInsulin.iobCalcForTreatment(this, time)
|
||||||
|
}
|
||||||
|
|
||||||
fun Bolus.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject =
|
fun Bolus.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject =
|
||||||
JSONObject()
|
JSONObject()
|
||||||
.put("eventType", if (type == Bolus.Type.SMB) TherapyEvent.Type.CORRECTION_BOLUS.text else TherapyEvent.Type.MEAL_BOLUS.text)
|
.put("eventType", if (type == Bolus.Type.SMB) TherapyEvent.Type.CORRECTION_BOLUS.text else TherapyEvent.Type.MEAL_BOLUS.text)
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package info.nightscout.androidaps.interfaces
|
||||||
|
|
||||||
|
interface Autotune {
|
||||||
|
|
||||||
|
fun aapsAutotune(daysBack: Int, autoSwitch: Boolean, profileToTune: String = ""): String
|
||||||
|
fun atLog(message: String)
|
||||||
|
|
||||||
|
var lastRunSuccess: Boolean
|
||||||
|
}
|
|
@ -148,6 +148,10 @@ interface Profile {
|
||||||
return (passed / 1000).toInt()
|
return (passed / 1000).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun milliSecFromMidnight(date: Long): Long {
|
||||||
|
val passed = DateTime(date).millisOfDay.toLong()
|
||||||
|
return passed
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Units conversion
|
* Units conversion
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -285,6 +285,7 @@ class Translator @Inject internal constructor(
|
||||||
Sources.Aaps -> TODO()
|
Sources.Aaps -> TODO()
|
||||||
*/
|
*/
|
||||||
Sources.Automation -> rh.gs(R.string.automation)
|
Sources.Automation -> rh.gs(R.string.automation)
|
||||||
|
Sources.Autotune -> rh.gs(R.string.autotune)
|
||||||
Sources.Loop -> rh.gs(R.string.loop)
|
Sources.Loop -> rh.gs(R.string.loop)
|
||||||
Sources.NSClient -> rh.gs(R.string.ns)
|
Sources.NSClient -> rh.gs(R.string.ns)
|
||||||
Sources.Pump -> rh.gs(R.string.pump)
|
Sources.Pump -> rh.gs(R.string.pump)
|
||||||
|
|
|
@ -108,6 +108,7 @@ class UserEntryMapper {
|
||||||
Announcement (UserEntry.Sources.Announcement),
|
Announcement (UserEntry.Sources.Announcement),
|
||||||
Actions (UserEntry.Sources.Actions),
|
Actions (UserEntry.Sources.Actions),
|
||||||
Automation (UserEntry.Sources.Automation),
|
Automation (UserEntry.Sources.Automation),
|
||||||
|
Autotune (UserEntry.Sources.Autotune),
|
||||||
BG (UserEntry.Sources.BG),
|
BG (UserEntry.Sources.BG),
|
||||||
Aidex (UserEntry.Sources.Aidex),
|
Aidex (UserEntry.Sources.Aidex),
|
||||||
Dexcom (UserEntry.Sources.Dexcom),
|
Dexcom (UserEntry.Sources.Dexcom),
|
||||||
|
|
|
@ -62,6 +62,7 @@ class UserEntryPresentationHelper @Inject constructor(
|
||||||
Sources.Announcement -> R.drawable.ic_cp_announcement
|
Sources.Announcement -> R.drawable.ic_cp_announcement
|
||||||
Sources.Actions -> R.drawable.ic_action
|
Sources.Actions -> R.drawable.ic_action
|
||||||
Sources.Automation -> R.drawable.ic_automation
|
Sources.Automation -> R.drawable.ic_automation
|
||||||
|
Sources.Autotune -> R.drawable.ic_autotune
|
||||||
Sources.BG -> R.drawable.ic_generic_cgm
|
Sources.BG -> R.drawable.ic_generic_cgm
|
||||||
Sources.Aidex -> R.drawable.ic_blooddrop_48
|
Sources.Aidex -> R.drawable.ic_blooddrop_48
|
||||||
Sources.Dexcom -> R.drawable.ic_dexcom_g6
|
Sources.Dexcom -> R.drawable.ic_dexcom_g6
|
||||||
|
|
|
@ -455,7 +455,6 @@
|
||||||
<string name="autotune_circadian_ic_isf_summary">Autotune nebude ladit cirkadiánní rozložení, tato možnost použije pouze průměrné hodnoty IC a ISF na váš cirkadiánní vstupní profil</string>
|
<string name="autotune_circadian_ic_isf_summary">Autotune nebude ladit cirkadiánní rozložení, tato možnost použije pouze průměrné hodnoty IC a ISF na váš cirkadiánní vstupní profil</string>
|
||||||
<string name="autotune_additional_log_title">Zahrnout další informace o protokolu pro ladění</string>
|
<string name="autotune_additional_log_title">Zahrnout další informace o protokolu pro ladění</string>
|
||||||
<string name="autotune_additional_log_summary">Zapněte, pouze pokud je to požadováno vývojářem, aby bylo možné odeslat další logy pro ladění pluginu Autotune</string>
|
<string name="autotune_additional_log_summary">Zapněte, pouze pokud je to požadováno vývojářem, aby bylo možné odeslat další logy pro ladění pluginu Autotune</string>
|
||||||
<string name="autotune_default_tune_days_summary">Výchozí počet dní, ze kterých mají být data zpracována Autotune (až xx)</string>
|
|
||||||
<string name="autotune_tunedprofile_name">Vyladěno</string>
|
<string name="autotune_tunedprofile_name">Vyladěno</string>
|
||||||
<string name="autotune_profile">Profil :</string>
|
<string name="autotune_profile">Profil :</string>
|
||||||
<string name="autotune_tune_days">Ladit dní:</string>
|
<string name="autotune_tune_days">Ladit dní:</string>
|
||||||
|
@ -464,7 +463,6 @@
|
||||||
<string name="autotune_select_profile">Vyberte profil pro ladění</string>
|
<string name="autotune_select_profile">Vyberte profil pro ladění</string>
|
||||||
<string name="autotune_ic_warning">Autotune funguje pouze s jedinou hodnotou IC, váš profil má %1$d hodnot. Průměrná hodnota je %2$.2fg/U</string>
|
<string name="autotune_ic_warning">Autotune funguje pouze s jedinou hodnotou IC, váš profil má %1$d hodnot. Průměrná hodnota je %2$.2fg/U</string>
|
||||||
<string name="autotune_isf_warning">Autotune funguje pouze s jedinou hodnotou ISF, váš profil má %1$d hodnot. Průměrná hodnota je %2$.1f%3$s/U</string>
|
<string name="autotune_isf_warning">Autotune funguje pouze s jedinou hodnotou ISF, váš profil má %1$d hodnot. Průměrná hodnota je %2$.1f%3$s/U</string>
|
||||||
<string name="autotune_error">Chyba vstupních dat, zkuste snížit počet dní</string>
|
|
||||||
<string name="autotune_warning_during_run">Autotune spuštěno, prosím buďte trpěliví</string>
|
<string name="autotune_warning_during_run">Autotune spuštěno, prosím buďte trpěliví</string>
|
||||||
<string name="autotune_warning_after_run">Před použitím výsledky pečlivě zkontrolujte!</string>
|
<string name="autotune_warning_after_run">Před použitím výsledky pečlivě zkontrolujte!</string>
|
||||||
<string name="autotune_partial_result">Částečný výsledek - vyladěn den %1$d / %2$d</string>
|
<string name="autotune_partial_result">Částečný výsledek - vyladěn den %1$d / %2$d</string>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<!-- General-->
|
<!-- General-->
|
||||||
<string name="refresh">Opdatér</string>
|
<string name="refresh">Opdatér</string>
|
||||||
<string name="error">Fejl</string>
|
<string name="error">Fejl</string>
|
||||||
|
<string name="save">Gem</string>
|
||||||
<string name="not_set_short">Ikke angivet</string>
|
<string name="not_set_short">Ikke angivet</string>
|
||||||
<string name="failedupdatebasalprofile">Opdatering af basal profil mislykkedes</string>
|
<string name="failedupdatebasalprofile">Opdatering af basal profil mislykkedes</string>
|
||||||
<string name="profile_set_ok">Basal profil i pumpen er opdateret</string>
|
<string name="profile_set_ok">Basal profil i pumpen er opdateret</string>
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
<string name="carbs">Kulhydrater</string>
|
<string name="carbs">Kulhydrater</string>
|
||||||
<string name="invalidprofile">Ugyldig profil !!!</string>
|
<string name="invalidprofile">Ugyldig profil !!!</string>
|
||||||
<string name="noprofileset">INGEN PROFIL SAT</string>
|
<string name="noprofileset">INGEN PROFIL SAT</string>
|
||||||
|
<string name="active"><![CDATA[<Active>]]></string>
|
||||||
<string name="date">Dato</string>
|
<string name="date">Dato</string>
|
||||||
<string name="units_label">Enheder</string>
|
<string name="units_label">Enheder</string>
|
||||||
<string name="dia_label">DIA</string>
|
<string name="dia_label">DIA</string>
|
||||||
|
@ -110,6 +112,7 @@
|
||||||
<string name="notes_label">Bemærkninger</string>
|
<string name="notes_label">Bemærkninger</string>
|
||||||
<string name="remove_button">Fjern</string>
|
<string name="remove_button">Fjern</string>
|
||||||
<string name="addnew">Tilføj ny</string>
|
<string name="addnew">Tilføj ny</string>
|
||||||
|
<string name="addnew_above">Tilføj ny ovenfor</string>
|
||||||
<string name="wrong_pump_data">Data kommer fra en anden pumpe. Skift pumpedriver for at nulstille pumpetilstand.</string>
|
<string name="wrong_pump_data">Data kommer fra en anden pumpe. Skift pumpedriver for at nulstille pumpetilstand.</string>
|
||||||
<!-- Constraints-->
|
<!-- Constraints-->
|
||||||
<string name="limitingbasalratio">Begrænser max IOB til %1$.2f E/t på grund af %2$s</string>
|
<string name="limitingbasalratio">Begrænser max IOB til %1$.2f E/t på grund af %2$s</string>
|
||||||
|
@ -132,7 +135,9 @@
|
||||||
<string name="location_not_found_message">Placeringen skal være aktiveret, hvis Bluetooth-opdagelsen skal fungere på nyere enheder. AAPS sporer ikke din placering, og det kan deaktiveres efter parring er lykkedes.</string>
|
<string name="location_not_found_message">Placeringen skal være aktiveret, hvis Bluetooth-opdagelsen skal fungere på nyere enheder. AAPS sporer ikke din placering, og det kan deaktiveres efter parring er lykkedes.</string>
|
||||||
<!-- Protection-->
|
<!-- Protection-->
|
||||||
<string name="wrongpassword">Forkert kodeord</string>
|
<string name="wrongpassword">Forkert kodeord</string>
|
||||||
|
<string name="wrongpin">Forkert pinkode</string>
|
||||||
<string name="passwords_dont_match">Kodeordene stemmer ikke overens</string>
|
<string name="passwords_dont_match">Kodeordene stemmer ikke overens</string>
|
||||||
|
<string name="pin_dont_match">PIN-koder ikke identiske</string>
|
||||||
<!-- Profile-->
|
<!-- Profile-->
|
||||||
<string name="basalprofilenotaligned">Basalværdier ikke angivet i hele timer: %1$s</string>
|
<string name="basalprofilenotaligned">Basalværdier ikke angivet i hele timer: %1$s</string>
|
||||||
<string name="minimalbasalvaluereplaced">Basal dosis erstattet af minimal understøttet dosis: %1$s</string>
|
<string name="minimalbasalvaluereplaced">Basal dosis erstattet af minimal understøttet dosis: %1$s</string>
|
||||||
|
@ -352,6 +357,7 @@
|
||||||
<string name="uel_stat_reset">STAT NULSTIL</string>
|
<string name="uel_stat_reset">STAT NULSTIL</string>
|
||||||
<string name="uel_delete_logs">SLET LOGS</string>
|
<string name="uel_delete_logs">SLET LOGS</string>
|
||||||
<string name="uel_delete_future_treatments">SLET FREMTIDIGE BEHANDLINGER</string>
|
<string name="uel_delete_future_treatments">SLET FREMTIDIGE BEHANDLINGER</string>
|
||||||
|
<string name="delete_future_treatments">Slet fremtidige behandlinger</string>
|
||||||
<string name="uel_export_settings">EKSPORTER INDSTILLINGER</string>
|
<string name="uel_export_settings">EKSPORTER INDSTILLINGER</string>
|
||||||
<string name="uel_import_settings">IMPORTER INDSTILLINGER</string>
|
<string name="uel_import_settings">IMPORTER INDSTILLINGER</string>
|
||||||
<string name="uel_reset_databases">NULSTIL DATABASER</string>
|
<string name="uel_reset_databases">NULSTIL DATABASER</string>
|
||||||
|
@ -423,7 +429,61 @@
|
||||||
<string name="bolus_ok" comment="26 characters max for translation">Bolus OK</string>
|
<string name="bolus_ok" comment="26 characters max for translation">Bolus OK</string>
|
||||||
<string name="pump_paired" comment="26 characters max for translation">Pumpe parret</string>
|
<string name="pump_paired" comment="26 characters max for translation">Pumpe parret</string>
|
||||||
<string name="insight_refresh_button" comment="26 characters max for translation">Insight Opdater-knap</string>
|
<string name="insight_refresh_button" comment="26 characters max for translation">Insight Opdater-knap</string>
|
||||||
|
<string name="a11y_min_button_description">fald %1$s af %2$s</string>
|
||||||
|
<string name="a11y_plus_button_description">stigning %1$s af %2$s</string>
|
||||||
|
<string name="formatPercent">%1$.0f%%</string>
|
||||||
|
<string name="basal">Basal</string>
|
||||||
|
<string name="basalpct">Basal %</string>
|
||||||
|
<string name="count_selected">%1$d valgt</string>
|
||||||
|
<string name="sort_label">Sortér</string>
|
||||||
|
<string name="remove_items">Fjern Emner</string>
|
||||||
|
<string name="sort_items">Sortér Emner</string>
|
||||||
|
<string name="remove_selected_items">Fjern valgte emner</string>
|
||||||
|
<string name="a11y_file">fil</string>
|
||||||
|
<string name="a11y_user">bruger</string>
|
||||||
<!-- Autotune -->
|
<!-- Autotune -->
|
||||||
|
<string name="autotune">Autotune</string>
|
||||||
|
<string name="autotune_description">Hjælp til potentielle justeringer af profil (ISF, KH-ratio og basalrater)</string>
|
||||||
|
<string name="autotune_shortname">AT</string>
|
||||||
|
<string name="autotune_settings">Autotune indstillinger</string>
|
||||||
|
<string name="autotune_auto_title">Automatisk profilskift</string>
|
||||||
|
<string name="autotune_auto_summary">Hvis aktiveret, vil Autotune automatisk opdatere og skifte til input-profil efter beregning ud fra en automatiseringsregel.</string>
|
||||||
|
<string name="autotune_categorize_uam_as_basal_title">Kategoriser UAM som basal</string>
|
||||||
|
<string name="autotune_categorize_uam_as_basal_summary">Aktiver kun hvis du har indtastet alle kulhydrater på pålidelig vis. Med denne indstilling vil pludselige stigninger set af Autotune, blive brugt til at anbefale ændringer af basal rate.</string>
|
||||||
|
<string name="autotune_default_tune_days_title">Antal dage med data</string>
|
||||||
|
<string name="autotune_circadian_ic_isf_title">Anvend gennemsnitligt resultat i døgnbaseret IC/ISF</string>
|
||||||
|
<string name="autotune_circadian_ic_isf_summary">Autotune vil ikke justere døgnrytme variationer, denne indstilling benytter gennemsnitlige værdier til justering af IC og ISF til din døgnrytme input profil</string>
|
||||||
|
<string name="autotune_additional_log_title">Inkludér flere logoplysninger for fejlfinding</string>
|
||||||
|
<string name="autotune_additional_log_summary">Aktivér kun hvis udviklerne beder dig om det, for at sende flere logoplysninger til at hjælpe med at fejlfinde Autotune plugin</string>
|
||||||
|
<string name="autotune_tunedprofile_name">Justeret</string>
|
||||||
|
<string name="autotune_profile">Profil :</string>
|
||||||
|
<string name="autotune_tune_days">Justerings dage :</string>
|
||||||
|
<string name="autotune_last_run">Sidst kørt :</string>
|
||||||
|
<string name="autotune_warning">Advarsel :</string>
|
||||||
|
<string name="autotune_select_profile">Vælg profil, der skal justeres</string>
|
||||||
|
<string name="autotune_ic_warning">Autotune fungerer med kun én IC-værdi, din profil har %1$d værdier. Gennemsnitsværdi er %2$.2fg/E</string>
|
||||||
|
<string name="autotune_isf_warning">Autotune virker med kun én ISF-værdi, din profil har %1$d værdier. Gennemsnitlig værdi er %2$.1f%3$s/E</string>
|
||||||
|
<string name="autotune_warning_during_run">Automatisk beregning startet, vær venligst tålmodig</string>
|
||||||
|
<string name="autotune_warning_after_run">Kontrollér resultaterne omhyggeligt, før du bruger dem!</string>
|
||||||
|
<string name="autotune_partial_result">Delvis resultat dag %1$d / %2$d justeret</string>
|
||||||
|
<string name="autotune_result">Resultat: %1$s</string>
|
||||||
|
<string name="autotune_param">Parameter</string>
|
||||||
|
<string name="autotune_percent">%</string>
|
||||||
|
<string name="autotune_missing">Mangler</string>
|
||||||
|
<string name="autotune_profile_name">Autotune profil %1$s</string>
|
||||||
|
<string name="autotune_run">Kør Autotune</string>
|
||||||
|
<string name="autotune_check_input_profile_button">Tjek input-profil</string>
|
||||||
|
<string name="autotune_compare_profile">Sammenlign profiler</string>
|
||||||
|
<string name="autotune_copy_localprofile_button">Kopier til lokal profil</string>
|
||||||
|
<string name="autotune_update_input_profile_button">Opdater input profil</string>
|
||||||
|
<string name="autotune_revert_input_profile_button">Gendan input profil</string>
|
||||||
|
<string name="autotune_copy_local_profile_message">Opret en ny lokal profil ud fra denne Autotune profil?</string>
|
||||||
|
<string name="autotune_update_local_profile_message">Opdater %1$s profil med Autotune profil?</string>
|
||||||
|
<string name="autotune_revert_local_profile_message">Gendan %1$s profil med Input-profilen?</string>
|
||||||
|
<string name="autotune_profile_invalid">Profil ugyldig</string>
|
||||||
|
<string name="autotune_run_without_autoswitch">Autotune kørt uden profilskift</string>
|
||||||
|
<string name="autotune_run_with_autoswitch">Autotune kørt, profilen er automatisk skiftet</string>
|
||||||
|
<string name="autotune_run_with_error">Fejl under sidste Autotune kørsel</string>
|
||||||
<plurals name="days">
|
<plurals name="days">
|
||||||
<item quantity="one">%1$d dag</item>
|
<item quantity="one">%1$d dag</item>
|
||||||
<item quantity="other">%1$d dage</item>
|
<item quantity="other">%1$d dage</item>
|
||||||
|
|
|
@ -455,7 +455,6 @@
|
||||||
<string name="autotune_circadian_ic_isf_summary">Autotune no afinará las variaciones circadianas, esta opción solo aplica los ajustes medios de IC e ISF a tu perfil circadiano de entrada</string>
|
<string name="autotune_circadian_ic_isf_summary">Autotune no afinará las variaciones circadianas, esta opción solo aplica los ajustes medios de IC e ISF a tu perfil circadiano de entrada</string>
|
||||||
<string name="autotune_additional_log_title">Incluir más información de registro para depuración</string>
|
<string name="autotune_additional_log_title">Incluir más información de registro para depuración</string>
|
||||||
<string name="autotune_additional_log_summary">Activar sólo si es solicitado por los desarrolladores, para enviar más registros y así ayudar a depurar el plugin Autotune</string>
|
<string name="autotune_additional_log_summary">Activar sólo si es solicitado por los desarrolladores, para enviar más registros y así ayudar a depurar el plugin Autotune</string>
|
||||||
<string name="autotune_default_tune_days_summary">Número predeterminado de días de datos a procesar por Autotune (hasta xx)</string>
|
|
||||||
<string name="autotune_tunedprofile_name">Ajustado</string>
|
<string name="autotune_tunedprofile_name">Ajustado</string>
|
||||||
<string name="autotune_profile">Perfil :</string>
|
<string name="autotune_profile">Perfil :</string>
|
||||||
<string name="autotune_tune_days">Días de ajuste:</string>
|
<string name="autotune_tune_days">Días de ajuste:</string>
|
||||||
|
@ -464,7 +463,6 @@
|
||||||
<string name="autotune_select_profile">Selecciona el perfil para a ajustar</string>
|
<string name="autotune_select_profile">Selecciona el perfil para a ajustar</string>
|
||||||
<string name="autotune_ic_warning">Autotune sólo funciona con un valor de IC. Tu perfil tiene %1$d valores. El valor promedio es %2$.2fg/U</string>
|
<string name="autotune_ic_warning">Autotune sólo funciona con un valor de IC. Tu perfil tiene %1$d valores. El valor promedio es %2$.2fg/U</string>
|
||||||
<string name="autotune_isf_warning">Autotune sólo funciona con un valor de ISF. Tu perfil tiene %1$d valores. El valor promedio es %2$.1f%3$s/U</string>
|
<string name="autotune_isf_warning">Autotune sólo funciona con un valor de ISF. Tu perfil tiene %1$d valores. El valor promedio es %2$.1f%3$s/U</string>
|
||||||
<string name="autotune_error">Error en los datos de entrada, intenta reducir el número de días</string>
|
|
||||||
<string name="autotune_warning_during_run">Cálculo de autototune iniciado, por favor ten paciencia</string>
|
<string name="autotune_warning_during_run">Cálculo de autototune iniciado, por favor ten paciencia</string>
|
||||||
<string name="autotune_warning_after_run">¡Comprueba los resultados cuidadosamente antes de usarlos!</string>
|
<string name="autotune_warning_after_run">¡Comprueba los resultados cuidadosamente antes de usarlos!</string>
|
||||||
<string name="autotune_partial_result">Resultado parcial día %1$d / %2$d afinado</string>
|
<string name="autotune_partial_result">Resultado parcial día %1$d / %2$d afinado</string>
|
||||||
|
|
|
@ -455,7 +455,6 @@
|
||||||
<string name="autotune_circadian_ic_isf_summary">Autotune ne réglera pas les variations circadiennes, cette option ne fait que appliquer le réglage moyen du G/I et de la SI à votre profil d\'entrée circadien</string>
|
<string name="autotune_circadian_ic_isf_summary">Autotune ne réglera pas les variations circadiennes, cette option ne fait que appliquer le réglage moyen du G/I et de la SI à votre profil d\'entrée circadien</string>
|
||||||
<string name="autotune_additional_log_title">Inclure plus d\'informations de log pour le débogage</string>
|
<string name="autotune_additional_log_title">Inclure plus d\'informations de log pour le débogage</string>
|
||||||
<string name="autotune_additional_log_summary">A n\'activer que sur demande du développeur pour envoyer plus d\'informations dans les fichiers log pour aider à déboguer le plugin Autotune</string>
|
<string name="autotune_additional_log_summary">A n\'activer que sur demande du développeur pour envoyer plus d\'informations dans les fichiers log pour aider à déboguer le plugin Autotune</string>
|
||||||
<string name="autotune_default_tune_days_summary">Nombre de jours de données par défaut à traiter par Autotune (jusqu\'à 30)</string>
|
|
||||||
<string name="autotune_tunedprofile_name">Tuned</string>
|
<string name="autotune_tunedprofile_name">Tuned</string>
|
||||||
<string name="autotune_profile">Profil :</string>
|
<string name="autotune_profile">Profil :</string>
|
||||||
<string name="autotune_tune_days">Nb jours :</string>
|
<string name="autotune_tune_days">Nb jours :</string>
|
||||||
|
@ -464,7 +463,6 @@
|
||||||
<string name="autotune_select_profile">Sélectionnez le profil à optimiser</string>
|
<string name="autotune_select_profile">Sélectionnez le profil à optimiser</string>
|
||||||
<string name="autotune_ic_warning">Autotune ne fonctionne qu\'avec une seule valeur G/I, votre profil a %1$d valeurs. La valeur moyenne est de %2$.2f g/U</string>
|
<string name="autotune_ic_warning">Autotune ne fonctionne qu\'avec une seule valeur G/I, votre profil a %1$d valeurs. La valeur moyenne est de %2$.2f g/U</string>
|
||||||
<string name="autotune_isf_warning">Autotune ne calcule qu\'une seule valeur de SI, votre profil a %1$d valeurs. La valeur moyenne est de %2$.1f %3$s/U</string>
|
<string name="autotune_isf_warning">Autotune ne calcule qu\'une seule valeur de SI, votre profil a %1$d valeurs. La valeur moyenne est de %2$.1f %3$s/U</string>
|
||||||
<string name="autotune_error">Erreur dans les données d\'entrée, essayez de relancer le calcul ou réduire le nombre de jours</string>
|
|
||||||
<string name="autotune_warning_during_run">Le calcul Autotune a commencé, veuillez patienter</string>
|
<string name="autotune_warning_during_run">Le calcul Autotune a commencé, veuillez patienter</string>
|
||||||
<string name="autotune_warning_after_run">Vérifiez attentivement les résultats avant de les utiliser!</string>
|
<string name="autotune_warning_after_run">Vérifiez attentivement les résultats avant de les utiliser!</string>
|
||||||
<string name="autotune_partial_result">Résultat partiel jour %1$d / %2$d calculé</string>
|
<string name="autotune_partial_result">Résultat partiel jour %1$d / %2$d calculé</string>
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
<string name="carbs">Karbohydrater</string>
|
<string name="carbs">Karbohydrater</string>
|
||||||
<string name="invalidprofile">Ugyldig profil!!!</string>
|
<string name="invalidprofile">Ugyldig profil!!!</string>
|
||||||
<string name="noprofileset">INGEN PROFIL VALGT</string>
|
<string name="noprofileset">INGEN PROFIL VALGT</string>
|
||||||
|
<string name="active"><![CDATA[<Active>]]></string>
|
||||||
<string name="date">Dato</string>
|
<string name="date">Dato</string>
|
||||||
<string name="units_label">Enheter</string>
|
<string name="units_label">Enheter</string>
|
||||||
<string name="dia_label">DIA</string>
|
<string name="dia_label">DIA</string>
|
||||||
|
@ -441,6 +442,48 @@
|
||||||
<string name="a11y_file">fil</string>
|
<string name="a11y_file">fil</string>
|
||||||
<string name="a11y_user">bruker</string>
|
<string name="a11y_user">bruker</string>
|
||||||
<!-- Autotune -->
|
<!-- Autotune -->
|
||||||
|
<string name="autotune">Autotune</string>
|
||||||
|
<string name="autotune_description">Hjelp til å justere profilen (insulinfølsomhet, karbohydratfaktor og basal doser)</string>
|
||||||
|
<string name="autotune_shortname">AT</string>
|
||||||
|
<string name="autotune_settings">Autotune innstillinger</string>
|
||||||
|
<string name="autotune_auto_title">Automatisering profilbytte</string>
|
||||||
|
<string name="autotune_auto_summary">Hvis det er aktivert så vil Autotune automatisk oppdatere profilen og skifte til denne etter beregninger fra en automatiserings regel.</string>
|
||||||
|
<string name="autotune_categorize_uam_as_basal_title">Kategoriser UAM (uannonsert måltid) som basal</string>
|
||||||
|
<string name="autotune_categorize_uam_as_basal_summary">Aktiver kun hvis du korrekt har angitt alle spiste karbohydrater. Med dette alternativet vil Autotune foreslå endringer i basal doser hvis den plutselig oppdager BS stigninger.</string>
|
||||||
|
<string name="autotune_default_tune_days_title">Antall dager med data</string>
|
||||||
|
<string name="autotune_circadian_ic_isf_title">Bruk gjennomsnittsverdier i cirkadisk IK/ISF</string>
|
||||||
|
<string name="autotune_circadian_ic_isf_summary">Autotune vil ikke justere cirkadiske variasjoner. Dette valget benytter gjennomsnittsverdier til å justere IK og ISF i din cirkadiske inngangsprofil</string>
|
||||||
|
<string name="autotune_additional_log_title">Inkluder mer logginformasjon for feilsøking</string>
|
||||||
|
<string name="autotune_additional_log_summary">Slå på kun dersom det er forespurt av utviklere for å sende mer logginformasjon for å hjelpe i feilsøking av Autotune</string>
|
||||||
|
<string name="autotune_tunedprofile_name">Innstilt</string>
|
||||||
|
<string name="autotune_profile">Profil :</string>
|
||||||
|
<string name="autotune_tune_days">Antall dager:</string>
|
||||||
|
<string name="autotune_last_run">Siste beregning :</string>
|
||||||
|
<string name="autotune_warning">Varsel:</string>
|
||||||
|
<string name="autotune_select_profile">Velg profil du vil justere</string>
|
||||||
|
<string name="autotune_ic_warning">Autotune fungerer bare med én IC-verdi, og din profil har %1$d verdier. Gjennomsnittlig verdi er %2$.2fg/E</string>
|
||||||
|
<string name="autotune_isf_warning">Autotune fungerer med bare én ISF verdi, og din profil har %1$d verdier. Gjennomsnittsverdi er %2$.1f%3$s/E</string>
|
||||||
|
<string name="autotune_warning_during_run">Har startet Autotune beregning, vennligst vent</string>
|
||||||
|
<string name="autotune_warning_after_run">Kontroller resultatene nøye før du bruker dem!</string>
|
||||||
|
<string name="autotune_partial_result">Delvis resultat dag %1$d / %2$d justert</string>
|
||||||
|
<string name="autotune_result">Resultat: %1$s</string>
|
||||||
|
<string name="autotune_param">Parametre</string>
|
||||||
|
<string name="autotune_percent">%</string>
|
||||||
|
<string name="autotune_missing">Mangler</string>
|
||||||
|
<string name="autotune_profile_name">Autotune profil %1$s</string>
|
||||||
|
<string name="autotune_run">Kjør Autotune</string>
|
||||||
|
<string name="autotune_check_input_profile_button">Sjekk inngangsprofil</string>
|
||||||
|
<string name="autotune_compare_profile">Sammenlign profiler</string>
|
||||||
|
<string name="autotune_copy_localprofile_button">Kopier til lokal profil</string>
|
||||||
|
<string name="autotune_update_input_profile_button">Oppdater inngangsprofil</string>
|
||||||
|
<string name="autotune_revert_input_profile_button">Tilbakestill inngangsprofil</string>
|
||||||
|
<string name="autotune_copy_local_profile_message">Opprette ny lokal profil fra denne Autotune profilen?</string>
|
||||||
|
<string name="autotune_update_local_profile_message">Oppdater %1$s profil med Autotune profil?</string>
|
||||||
|
<string name="autotune_revert_local_profile_message">Tilbakestill %1$s profil med inngangsprofil?</string>
|
||||||
|
<string name="autotune_profile_invalid">Ugyldig profil</string>
|
||||||
|
<string name="autotune_run_without_autoswitch">Autotune utført uten profilbytte</string>
|
||||||
|
<string name="autotune_run_with_autoswitch">Autotune utført og profil automatisk skiftet ut</string>
|
||||||
|
<string name="autotune_run_with_error">Feil oppdaget under siste Autotune kjøring</string>
|
||||||
<plurals name="days">
|
<plurals name="days">
|
||||||
<item quantity="one">%1$d dag</item>
|
<item quantity="one">%1$d dag</item>
|
||||||
<item quantity="other">%1$d dager</item>
|
<item quantity="other">%1$d dager</item>
|
||||||
|
|
|
@ -450,12 +450,14 @@
|
||||||
<string name="autotune_auto_summary">Pokiaľ je táto voľba povolená, Autotune po výpočte automaticky aktualizuje a prepne na vstupný profil zadaný v pravidle automatizácie.</string>
|
<string name="autotune_auto_summary">Pokiaľ je táto voľba povolená, Autotune po výpočte automaticky aktualizuje a prepne na vstupný profil zadaný v pravidle automatizácie.</string>
|
||||||
<string name="autotune_categorize_uam_as_basal_title">Započítavať UAM ako bazál</string>
|
<string name="autotune_categorize_uam_as_basal_title">Započítavať UAM ako bazál</string>
|
||||||
<string name="autotune_categorize_uam_as_basal_summary">Povoľte iba v prípade, že ste dôsledne zadávali všetky prijaté sacharidy. S touto možnosťou bude náhly vzostup detekovaný funkciou Autotune, použitý pre doporučovanie zmien bazálu.</string>
|
<string name="autotune_categorize_uam_as_basal_summary">Povoľte iba v prípade, že ste dôsledne zadávali všetky prijaté sacharidy. S touto možnosťou bude náhly vzostup detekovaný funkciou Autotune, použitý pre doporučovanie zmien bazálu.</string>
|
||||||
|
<string name="autotune_tune_insulin_curve_title">Ladiť inzulínovú krivku</string>
|
||||||
|
<string name="autotune_tune_insulin_curve_summary">Aktivuj iba ak používaš voliteľný vrchol. Táto možnosť má vyladiť vrchol a trvanie DIA</string>
|
||||||
<string name="autotune_default_tune_days_title">Počet dní s dátami</string>
|
<string name="autotune_default_tune_days_title">Počet dní s dátami</string>
|
||||||
<string name="autotune_circadian_ic_isf_title">Použi priemerný výsledok v cirkadiánnom IC/ISF</string>
|
<string name="autotune_circadian_ic_isf_title">Použi priemerný výsledok v cirkadiánnom IC/ISF</string>
|
||||||
<string name="autotune_circadian_ic_isf_summary">Autotune nebude ladiť cirkadiánne rozloženie, táto možnosť použije iba priemerné ladenie IC a ISF na váš cirkadiánný vstupní profil</string>
|
<string name="autotune_circadian_ic_isf_summary">Autotune nebude ladiť cirkadiánne rozloženie, táto možnosť použije iba priemerné ladenie IC a ISF na váš cirkadiánný vstupní profil</string>
|
||||||
<string name="autotune_additional_log_title">Zahrnúť ďalšie informácie o protokole pre ladenie</string>
|
<string name="autotune_additional_log_title">Zahrnúť ďalšie informácie o protokole pre ladenie</string>
|
||||||
<string name="autotune_additional_log_summary">Zapnite, iba pokiaľ je to požadované vývojárom, aby bolo možné odoslať dalšie logy pre ladenie modulu Autotune</string>
|
<string name="autotune_additional_log_summary">Zapnite, iba pokiaľ je to požadované vývojárom, aby bolo možné odoslať dalšie logy pre ladenie modulu Autotune</string>
|
||||||
<string name="autotune_default_tune_days_summary">Štandardný počet dní, z ktorých majú byť dáta spracované Autotune (až do xx)</string>
|
<string name="autotune_default_tune_days_summary">Štandardný počet dní, z ktorých majú byť dáta spracované Autotune (až do 30)</string>
|
||||||
<string name="autotune_tunedprofile_name">Vyladené</string>
|
<string name="autotune_tunedprofile_name">Vyladené</string>
|
||||||
<string name="autotune_profile">Profil :</string>
|
<string name="autotune_profile">Profil :</string>
|
||||||
<string name="autotune_tune_days">Ladiť dni:</string>
|
<string name="autotune_tune_days">Ladiť dni:</string>
|
||||||
|
@ -464,7 +466,7 @@
|
||||||
<string name="autotune_select_profile">Vyberte profil pre ladenie</string>
|
<string name="autotune_select_profile">Vyberte profil pre ladenie</string>
|
||||||
<string name="autotune_ic_warning">Autotune funguje iba s jedinou hodnotou IC, váš profil má %1$d hodnôt. Priemerná hodnota je %2$.2fg/JI</string>
|
<string name="autotune_ic_warning">Autotune funguje iba s jedinou hodnotou IC, váš profil má %1$d hodnôt. Priemerná hodnota je %2$.2fg/JI</string>
|
||||||
<string name="autotune_isf_warning">Autotune funguje iba s jedinou hodnotou IC, váš profil má %1$d hodnôt. Priemerná hodnota je %2$.1fg/JI</string>
|
<string name="autotune_isf_warning">Autotune funguje iba s jedinou hodnotou IC, váš profil má %1$d hodnôt. Priemerná hodnota je %2$.1fg/JI</string>
|
||||||
<string name="autotune_error">Chyba vstupných dát, skúste znížiť počet dní</string>
|
<string name="autotune_error">Chyba vstupných dát, skúste znova spustiť Autotune, alebo znížte počet dní</string>
|
||||||
<string name="autotune_warning_during_run">Autotune spustený, prosím buďte trpezliví</string>
|
<string name="autotune_warning_during_run">Autotune spustený, prosím buďte trpezliví</string>
|
||||||
<string name="autotune_warning_after_run">Pred použitím výsledky starostlivo skontrolujte!</string>
|
<string name="autotune_warning_after_run">Pred použitím výsledky starostlivo skontrolujte!</string>
|
||||||
<string name="autotune_partial_result">Čiastočný výsledok - vyladený deň %1$d / %2$d</string>
|
<string name="autotune_partial_result">Čiastočný výsledok - vyladený deň %1$d / %2$d</string>
|
||||||
|
|
|
@ -455,7 +455,6 @@
|
||||||
<string name="autotune_circadian_ic_isf_summary">OtoAyar sirkadiyen varyasyonları ayarlamaz, bu seçenek sirkadiyen giriş profilinize yalnızca IC ve İDF\'nün ortalama ayarını uygular</string>
|
<string name="autotune_circadian_ic_isf_summary">OtoAyar sirkadiyen varyasyonları ayarlamaz, bu seçenek sirkadiyen giriş profilinize yalnızca IC ve İDF\'nün ortalama ayarını uygular</string>
|
||||||
<string name="autotune_additional_log_title">Hata ayıklama için daha fazla günlük bilgisi ekleyin</string>
|
<string name="autotune_additional_log_title">Hata ayıklama için daha fazla günlük bilgisi ekleyin</string>
|
||||||
<string name="autotune_additional_log_summary">OtoAyar eklentisinde hata ayıklamaya yardımcı olmak için yalnızca geliştirici tarafından daha fazla günlük bilgisi göndermesi istenirse açın</string>
|
<string name="autotune_additional_log_summary">OtoAyar eklentisinde hata ayıklamaya yardımcı olmak için yalnızca geliştirici tarafından daha fazla günlük bilgisi göndermesi istenirse açın</string>
|
||||||
<string name="autotune_default_tune_days_summary">OtoAyar tarafından işlenecek varsayılan veri gün sayısı (en fazla xx)</string>
|
|
||||||
<string name="autotune_tunedprofile_name">Ayarlandı</string>
|
<string name="autotune_tunedprofile_name">Ayarlandı</string>
|
||||||
<string name="autotune_profile">Profil :</string>
|
<string name="autotune_profile">Profil :</string>
|
||||||
<string name="autotune_tune_days">Ayar günleri:</string>
|
<string name="autotune_tune_days">Ayar günleri:</string>
|
||||||
|
@ -464,7 +463,6 @@
|
||||||
<string name="autotune_select_profile">Ayarlanacak profili seçin</string>
|
<string name="autotune_select_profile">Ayarlanacak profili seçin</string>
|
||||||
<string name="autotune_ic_warning">OtoAyar yalnızca bir IC değeriyle çalışır, profilinizde %1$d değer mevcut. Ortalama değer: %2$.2fg/Ü</string>
|
<string name="autotune_ic_warning">OtoAyar yalnızca bir IC değeriyle çalışır, profilinizde %1$d değer mevcut. Ortalama değer: %2$.2fg/Ü</string>
|
||||||
<string name="autotune_isf_warning">OtoAyar yalnızca bir İDF değeriyle çalışır, profilinizde %1$d değer mevcut. Ortalama değer: %2$.1f%3$s/Ü</string>
|
<string name="autotune_isf_warning">OtoAyar yalnızca bir İDF değeriyle çalışır, profilinizde %1$d değer mevcut. Ortalama değer: %2$.1f%3$s/Ü</string>
|
||||||
<string name="autotune_error">Giriş verilerinde hata, gün sayısını azaltmaya çalışın</string>
|
|
||||||
<string name="autotune_warning_during_run">OtoAyar hesaplaması başladı, lütfen sabırlı olun</string>
|
<string name="autotune_warning_during_run">OtoAyar hesaplaması başladı, lütfen sabırlı olun</string>
|
||||||
<string name="autotune_warning_after_run">Kullanmadan önce sonuçları dikkatlice kontrol edin!</string>
|
<string name="autotune_warning_after_run">Kullanmadan önce sonuçları dikkatlice kontrol edin!</string>
|
||||||
<string name="autotune_partial_result">Kısmi sonuç günü %1$d / %2$d ayarlandı</string>
|
<string name="autotune_partial_result">Kısmi sonuç günü %1$d / %2$d ayarlandı</string>
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
<string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string>
|
<string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string>
|
||||||
<string name="key_autotune_auto" translatable="false">autotune_auto</string>
|
<string name="key_autotune_auto" translatable="false">autotune_auto</string>
|
||||||
<string name="key_autotune_categorize_uam_as_basal" translatable="false">categorize_uam_as_basal</string>
|
<string name="key_autotune_categorize_uam_as_basal" translatable="false">categorize_uam_as_basal</string>
|
||||||
|
<string name="key_autotune_tune_insulin_curve" translatable="false">autotune_tune_insulin_curve</string>
|
||||||
<string name="key_autotune_default_tune_days" translatable="false">autotune_default_tune_days</string>
|
<string name="key_autotune_default_tune_days" translatable="false">autotune_default_tune_days</string>
|
||||||
<string name="key_autotune_circadian_ic_isf" translatable="false">autotune_circadian_ic_isf</string>
|
<string name="key_autotune_circadian_ic_isf" translatable="false">autotune_circadian_ic_isf</string>
|
||||||
<string name="key_autotune_additional_log" translatable="false">autotune_additional_log</string>
|
<string name="key_autotune_additional_log" translatable="false">autotune_additional_log</string>
|
||||||
|
@ -554,12 +555,14 @@
|
||||||
<string name="autotune_auto_summary">If enabled, Autotune will automatically update and switch to input profile after calculation from an automation rule.</string>
|
<string name="autotune_auto_summary">If enabled, Autotune will automatically update and switch to input profile after calculation from an automation rule.</string>
|
||||||
<string name="autotune_categorize_uam_as_basal_title">Categorize UAM as basal</string>
|
<string name="autotune_categorize_uam_as_basal_title">Categorize UAM as basal</string>
|
||||||
<string name="autotune_categorize_uam_as_basal_summary">Enable only if you have reliably entered all carbs eaten, with this option sudden rises seen by Autotune will be used to recommend changes to the basal rate.</string>
|
<string name="autotune_categorize_uam_as_basal_summary">Enable only if you have reliably entered all carbs eaten, with this option sudden rises seen by Autotune will be used to recommend changes to the basal rate.</string>
|
||||||
|
<string name="autotune_tune_insulin_curve_title">Tune insulin curve</string>
|
||||||
|
<string name="autotune_tune_insulin_curve_summary">Enable only if you use free peak. This option will tune peak and DIA durations</string>
|
||||||
<string name="autotune_default_tune_days_title">Number of days of data</string>
|
<string name="autotune_default_tune_days_title">Number of days of data</string>
|
||||||
<string name="autotune_circadian_ic_isf_title">Apply average result in circadian IC/ISF</string>
|
<string name="autotune_circadian_ic_isf_title">Apply average result in circadian IC/ISF</string>
|
||||||
<string name="autotune_circadian_ic_isf_summary">Autotune will not tune circadian variations, this option only apply the average tuning of IC and ISF to your circadian input profile</string>
|
<string name="autotune_circadian_ic_isf_summary">Autotune will not tune circadian variations, this option only apply the average tuning of IC and ISF to your circadian input profile</string>
|
||||||
<string name="autotune_additional_log_title">Include more log information for debugging</string>
|
<string name="autotune_additional_log_title">Include more log information for debugging</string>
|
||||||
<string name="autotune_additional_log_summary">Switch on only if requested by dev to send more log information to help debugging Autotune plugin</string>
|
<string name="autotune_additional_log_summary">Switch on only if requested by dev to send more log information to help debugging Autotune plugin</string>
|
||||||
<string name="autotune_default_tune_days_summary">Default number of days of data to be processed by Autotune (up to xx)</string>
|
<string name="autotune_default_tune_days_summary">Default number of days of data to be processed by Autotune (up to 30)</string>
|
||||||
<string name="autotune_tunedprofile_name">Tuned</string>
|
<string name="autotune_tunedprofile_name">Tuned</string>
|
||||||
<string name="autotune_profile">Profile :</string>
|
<string name="autotune_profile">Profile :</string>
|
||||||
<string name="autotune_tune_days">Tune days :</string>
|
<string name="autotune_tune_days">Tune days :</string>
|
||||||
|
@ -568,7 +571,7 @@
|
||||||
<string name="autotune_select_profile">Select profile to tune</string>
|
<string name="autotune_select_profile">Select profile to tune</string>
|
||||||
<string name="autotune_ic_warning">Autotune works with only one IC value, your profile has %1$d values. Average value is %2$.2fg/U</string>
|
<string name="autotune_ic_warning">Autotune works with only one IC value, your profile has %1$d values. Average value is %2$.2fg/U</string>
|
||||||
<string name="autotune_isf_warning">Autotune works with only one ISF value, your profile has %1$d values. Average value is %2$.1f%3$s/U</string>
|
<string name="autotune_isf_warning">Autotune works with only one ISF value, your profile has %1$d values. Average value is %2$.1f%3$s/U</string>
|
||||||
<string name="autotune_error">Error in input data, try to reduce the number of days</string>
|
<string name="autotune_error">Error in input data, try to run again autotune or reduce the number of days</string>
|
||||||
<string name="autotune_warning_during_run">Autotune calculation started, please be patient</string>
|
<string name="autotune_warning_during_run">Autotune calculation started, please be patient</string>
|
||||||
<string name="autotune_warning_after_run">Check the results carefully before using it!</string>
|
<string name="autotune_warning_after_run">Check the results carefully before using it!</string>
|
||||||
<string name="autotune_partial_result">Partial result day %1$d / %2$d tuned</string>
|
<string name="autotune_partial_result">Partial result day %1$d / %2$d tuned</string>
|
||||||
|
@ -589,8 +592,9 @@
|
||||||
<string name="autotune_profile_invalid">Profile invalid</string>
|
<string name="autotune_profile_invalid">Profile invalid</string>
|
||||||
<string name="autotune_log_title" translatable="false">|Param|Profile|Tuned|%/Miss.\n</string>
|
<string name="autotune_log_title" translatable="false">|Param|Profile|Tuned|%/Miss.\n</string>
|
||||||
<string name="autotune_log_separator" translatable="false">+------------------------------------------\n</string>
|
<string name="autotune_log_separator" translatable="false">+------------------------------------------\n</string>
|
||||||
<string name="autotune_log_isf" translatable="false">| %1$4.4s |\t%2$3.3f |\t%3$3.3f |\n</string>
|
<string name="autotune_log_peak" translatable="false">| %1$4.4s |\t%2$d |\t%3$d |\n</string>
|
||||||
<string name="autotune_log_ic" translatable="false">| %1$4.4s |\t%2$3.3f |\t%3$3.3f |\n</string>
|
<string name="autotune_log_dia" translatable="false">| %1$4.4s |\t%2$3.1f |\t%3$3.1f |\n</string>
|
||||||
|
<string name="autotune_log_ic_isf" translatable="false">| %1$4.4s | %2$3.3f |\t%3$3.3f |\n</string>
|
||||||
<string name="autotune_log_basal" translatable="false">|\t%1$02.0f\t| %2$3.3f |%3$3.3f\t| %5$.0f%% / %4$d\n</string>
|
<string name="autotune_log_basal" translatable="false">|\t%1$02.0f\t| %2$3.3f |%3$3.3f\t| %5$.0f%% / %4$d\n</string>
|
||||||
<string name="autotune_log_sum_basal" translatable="false">|\t∑\t|\t%1$3.1f |\t%2$3.1f |\n</string>
|
<string name="autotune_log_sum_basal" translatable="false">|\t∑\t|\t%1$3.1f |\t%2$3.1f |\n</string>
|
||||||
<string name="autotune_run_without_autoswitch">Autotune runned without profile switch</string>
|
<string name="autotune_run_without_autoswitch">Autotune runned without profile switch</string>
|
||||||
|
|
|
@ -1,5 +1,120 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="danars_pairing">Parring</string>
|
||||||
|
<string name="danars_nodeviceavailable">Ingen enhed fundet indtil videre</string>
|
||||||
|
<string name="danars_pairingok">Parring OK</string>
|
||||||
|
<string name="danars_pairingtimedout">Parring fik timeout</string>
|
||||||
|
<string name="danars_waitingforpairing">Venter på parring på pumpen</string>
|
||||||
|
<string name="danarspump">Dana-i/RS</string>
|
||||||
|
<string name="danarspump_shortname">Dana</string>
|
||||||
|
<string name="description_pump_dana_rs">Pumpeintegration til DANA Diabecare RS og Dana-i pumper</string>
|
||||||
|
<string name="maxbolusviolation">Max bolus overtrædelse</string>
|
||||||
|
<string name="commanderror">Kommando fejl</string>
|
||||||
|
<string name="speederror">Hastighedsfejl</string>
|
||||||
|
<string name="insulinlimitviolation">Overtrædelse af insulingrænse</string>
|
||||||
|
<string name="boluserrorcode">Ønsket: %1$.2fU Afgivet: %2$.2fU Fejlkode: %3$s</string>
|
||||||
|
<string name="danar_valuenotsetproperly">Værdi ikke angivet korrekt</string>
|
||||||
|
<string name="danar_setbasalstep001">Indstil basal trin til 0,01 E/t</string>
|
||||||
|
<string name="resetpairing">Nulstil parringsinformation?</string>
|
||||||
|
<string name="dana_model">%1$s\nModel: %2$02X\nProtokol: %3$02X\nKode: %4$02X</string>
|
||||||
|
<string name="processinghistory">Behandler begivenhed</string>
|
||||||
|
<string name="danar_enableextendedbolus">Aktiver forlængede bolusser på pumpe</string>
|
||||||
|
<string name="overview_bolusprogress_delivered">Afgivet</string>
|
||||||
|
<string name="overview_bolusprogress_stoped">Stoppet</string>
|
||||||
|
<string name="unsupportedfirmware">Ikke understøttet pumpe firmware</string>
|
||||||
|
<string name="pumperror">Pumpe Fejl</string>
|
||||||
|
<string name="lowbattery">Lavt batteri</string>
|
||||||
|
<string name="basalcompare">Leverer mindre end forudindstillet basal rate</string>
|
||||||
|
<string name="pumpshutdown">Pumpe Nedlukning</string>
|
||||||
|
<string name="batterydischarged">Pumpe batteri afladet</string>
|
||||||
|
<string name="occlusion">Tilstopning</string>
|
||||||
|
<string name="emptyreservoir">Tomt reservoir</string>
|
||||||
|
<string name="checkshaft">Kontrollér aksel</string>
|
||||||
|
<string name="basalmax">Basal max</string>
|
||||||
|
<string name="dailymax">Daglig max</string>
|
||||||
|
<string name="bloodsugarmeasurementalert">Advarsel om måling af blodsukker</string>
|
||||||
|
<string name="remaininsulinalert">Resterende insulin niveau</string>
|
||||||
|
<string name="missedbolus">Glemt bolus</string>
|
||||||
|
<string name="invalidpairing">Ugyldig parringsinformation. Anmoder om ny parring</string>
|
||||||
|
<string name="gettingpumpstatus">Henter pumpestatus</string>
|
||||||
|
<string name="gettingextendedbolusstatus">Henter forlænget bolusstatus</string>
|
||||||
|
<string name="gettingbolusstatus">Henter bolus status</string>
|
||||||
|
<string name="gettingtempbasalstatus">Få midlertidig basal status</string>
|
||||||
|
<string name="gettingpumpsettings">Henter pumpeindstillinger</string>
|
||||||
|
<string name="gettingpumptime">Henter pumpetid</string>
|
||||||
|
<string name="largetimedifftitle">Stor Tidsforskel</string>
|
||||||
|
<string name="largetimediff">Stor tidsforskel:\nTid i pumpen varierer med mere end 1,5 time.\nJustér venligst tiden manuelt på pumpen og sørg for, at læsning af historikken fra pumpen ikke forårsager uventet adfærd.\nHvis det er muligt; fjern historik fra pumpen før du ændrer tiden eller deaktiverer closed loop i én DIA efter den sidste forkerte historik post, men minimum én DIA fra nu.</string>
|
||||||
|
<string name="pairfirst">Par venligst din pumpe med din telefon!</string>
|
||||||
|
<string name="approachingdailylimit">Nærmer sig daglig insulingrænse</string>
|
||||||
|
<string name="startingbolus">Starter bolus afgivelse</string>
|
||||||
|
<string name="waitingforestimatedbolusend">Venter på bolusafslutningen. Mangler %1$d sek.</string>
|
||||||
|
<string name="stoppingtempbasal">Stopper midlertidig basal</string>
|
||||||
|
<string name="settingextendedbolus">Indstiller forlænget bolus</string>
|
||||||
|
<string name="stoppingextendedbolus">Stopper forlænget bolus</string>
|
||||||
|
<string name="updatingbasalrates">Opdaterer basal rater</string>
|
||||||
|
<string name="settingtempbasal">Indstiller midlertidig basal</string>
|
||||||
|
<string name="waitingfortimesynchronization">Venter på tidssynkronisering (%1$d sek)</string>
|
||||||
|
<string name="wrongpumppassword">Forkert pumpe kodeord!</string>
|
||||||
|
<string name="danar_history_alarm">Alarmer</string>
|
||||||
|
<string name="danar_history_basalhours">Basal Timer</string>
|
||||||
|
<string name="danar_history_bolus">Bolusser</string>
|
||||||
|
<string name="danar_history_carbohydrates">Kulhydrater</string>
|
||||||
|
<string name="danar_history_dailyinsulin">Daglig insulin</string>
|
||||||
|
<string name="danar_history_errors">Fejl</string>
|
||||||
<string name="danar_history_glucose">Glukose</string>
|
<string name="danar_history_glucose">Glukose</string>
|
||||||
|
<string name="danar_history_refill">Genopfyld</string>
|
||||||
|
<string name="danar_history_syspend">Suspendér</string>
|
||||||
|
<string name="danar_history_prime">Klargør</string>
|
||||||
|
<string name="danar_useroptions">Brugerindstillinger</string>
|
||||||
|
<string name="danar_timedisplay">Visning af tidsformat</string>
|
||||||
|
<string name="danar_buttonscroll">Knap rulning</string>
|
||||||
|
<string name="danar_beep">Bip ved tryk på knap</string>
|
||||||
<string name="danar_pumpalarm">Alarm</string>
|
<string name="danar_pumpalarm">Alarm</string>
|
||||||
|
<string name="danar_pumpalarm_sound">Lyd</string>
|
||||||
|
<string name="danar_pumpalarm_vibrate">Vibrér</string>
|
||||||
|
<string name="danar_pumpalarm_both">Begge</string>
|
||||||
|
<string name="danar_screentimeout">LCD tændt tid [seconds]</string>
|
||||||
|
<string name="danar_backlight">Bagbelysning tændt tid [seconds]</string>
|
||||||
|
<string name="danar_glucoseunits">Glukose enheder</string>
|
||||||
|
<string name="danar_shutdown">Nedlukning [hours]</string>
|
||||||
|
<string name="danar_lowreservoir">Lavt reservoir [Units]</string>
|
||||||
|
<string name="danar_saveuseroptions">Gem indstillinger på pumpen</string>
|
||||||
|
<string name="description_pump_dana_r">Pumpeintegration til DANA Diabecare R pumper</string>
|
||||||
|
<string name="description_pump_dana_r_korean">Pumpeintegration til DANA Diabecare R pumper, koreansk version</string>
|
||||||
|
<string name="description_pump_dana_r_v2">Pumpeintegration til DANA Diabecare R pumper med opgraderet firmware (v2)</string>
|
||||||
|
<string name="danarpump_shortname">DANA</string>
|
||||||
|
<string name="nobtadapter">Ingen bluetooth-adapter fundet</string>
|
||||||
|
<string name="devicenotfound">Valgte enhed ikke fundet</string>
|
||||||
|
<string name="danar_switchtouhmode">Skift tilstand fra E/d til E/t på pumpen</string>
|
||||||
|
<string name="danarkoreanpump">DanaR Koreansk</string>
|
||||||
|
<string name="danarpump">DanaR</string>
|
||||||
|
<string name="pumpdrivercorrected">Pumpe driver korrigeret</string>
|
||||||
|
<string name="danarv2pump">DanaRv2</string>
|
||||||
|
<string name="danar_disableeasymode">Deaktivér EasyUI-tilstand i pumpen</string>
|
||||||
|
<string name="profile_set_failed">Indstilling af basal profil mislykkedes</string>
|
||||||
|
<string name="danar_bluetooth_status">Bluetooth Status</string>
|
||||||
|
<string name="danar_iob_label">Pumpe IOB</string>
|
||||||
|
<string name="virtualpump_firmware_label">Firmware</string>
|
||||||
|
<string name="danar_pump_settings">Dana pumpe indstillinger</string>
|
||||||
|
<string name="timeformat12h">12t</string>
|
||||||
|
<string name="timeformat24h">24t</string>
|
||||||
|
<string name="option_on">Tændt</string>
|
||||||
|
<string name="option_off">Slukket</string>
|
||||||
|
<string name="danar_bt_name_title">DanaR Bluetooth enhed</string>
|
||||||
|
<string name="danars_password_title">Pumpe kodeord (kun v1)</string>
|
||||||
|
<string name="danar_password_title">Pumpe kodeord</string>
|
||||||
|
<string name="danar_useextended_title">Brug forlængede bolusser til >200%%</string>
|
||||||
|
<string name="bolusspeed">Bolus hastighed</string>
|
||||||
|
<string name="selectedpump">Valgt pumpe</string>
|
||||||
|
<string name="rs_loginsulinchange_title">Log reservoir ændring</string>
|
||||||
|
<string name="rs_loginsulinchange_summary">Tilføj \"Insulin Skift\" begivenhed til careportal, når de registreres i historik</string>
|
||||||
|
<string name="rs_logcanulachange_title">Log indstik skift</string>
|
||||||
|
<string name="rs_logcanulachange_summary">Tilføj \"Indstik skift\" begivenhed til careportal, når de registreres i historik</string>
|
||||||
|
<string name="pin1">PIN1</string>
|
||||||
|
<string name="pin2">PIN2</string>
|
||||||
|
<string name="press_ok_on_the_pump">Tryk OK på pumpen\nog angiv 2 viste numre\nHold skærmen tændt på pumpen ved at trykke på minus knappen, indtil du er færdig med at indtaste kode.</string>
|
||||||
|
<string name="num1pin">1: (12 cifre)</string>
|
||||||
|
<string name="num2pin">2: (8 cifre)</string>
|
||||||
|
<string name="basal_bolus_step">Basal/bolus trin</string>
|
||||||
|
<string name="carbs_store_error">Kulhydrater blev sandsynligvis ikke gemt korrekt. Tjek manuelt og gem igen, hvis det er nødvendigt.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -138,6 +138,7 @@ data class UserEntry(
|
||||||
Announcement,
|
Announcement,
|
||||||
Actions, //From Actions plugin
|
Actions, //From Actions plugin
|
||||||
Automation, //From Automation plugin
|
Automation, //From Automation plugin
|
||||||
|
Autotune, //From Autotune plugin
|
||||||
BG, //From BG plugin => Add One Source per BG Source for Calibration or Sensor Change
|
BG, //From BG plugin => Add One Source per BG Source for Calibration or Sensor Change
|
||||||
Aidex,
|
Aidex,
|
||||||
Dexcom,
|
Dexcom,
|
||||||
|
|
|
@ -1,6 +1,157 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="resetpairing">Nulstil parring</string>
|
||||||
|
<string name="diaconn_nodeviceavailable">Ingen enhed tilgængelig</string>
|
||||||
|
<string name="diaconn_pairing">Diaconn Pumpe Parring</string>
|
||||||
|
<string name="pumperror">Pumpe Fejl</string>
|
||||||
|
<string name="diaconn_g8_history_alarm">Alarmer</string>
|
||||||
|
<string name="diaconn_g8_history_basalhours">Basal Timer</string>
|
||||||
|
<string name="diaconn_g8_history_bolus">Bolusser</string>
|
||||||
|
<string name="diaconn_g8_history_dailyinsulin">Daglig insulin</string>
|
||||||
|
<string name="diaconn_g8_history_errors">Fejl</string>
|
||||||
|
<string name="diaconn_g8_history_prime">Klargør</string>
|
||||||
|
<string name="diaconn_g8_history_refill">Genopfyld</string>
|
||||||
|
<string name="diaconn_g8_history_suspend">Suspendér</string>
|
||||||
|
<string name="diaconn_g8_pairingok">Parring OK</string>
|
||||||
|
<string name="diaconn_g8_waitingforpairing">Venter på parring</string>
|
||||||
|
<string name="diaconn_firmware_version">Version</string>
|
||||||
|
<string name="invalidpairing">Ugyldig parringsinformation. Anmoder om ny parring</string>
|
||||||
|
<string name="gettingpumpsettings">Henter pumpeindstillinger</string>
|
||||||
|
<string name="gettingpumptime">Henter pumpetid</string>
|
||||||
|
<string name="largetimediff">Stor tidsforskel:\nTid i pumpen varierer med mere end 1,5 time.\nJustér venligst tiden manuelt på pumpen og sørg for, at læsning af historikken fra pumpen ikke forårsager uventet adfærd.\nHvis det er muligt; fjern historik fra pumpen før du ændrer tiden eller deaktiverer closed loop i én DIA efter den sidste forkerte historik post, men minimum én DIA fra nu.</string>
|
||||||
|
<string name="largetimedifftitle">Stor tidsforskel</string>
|
||||||
|
<string name="approachingdailylimit">Nærmer sig daglig insulingrænse</string>
|
||||||
|
<string name="startingbolus">Starter bolus afgivelse</string>
|
||||||
|
<string name="waitingforestimatedbolusend">Venter på estimerede bolusafslutning</string>
|
||||||
|
<string name="gettingbolusstatus">Henter bolus status</string>
|
||||||
|
<string name="stoppingtempbasal">Stopper midlertidig basal</string>
|
||||||
|
<string name="settingtempbasal">Indstiller midlertidig basal</string>
|
||||||
|
<string name="settingextendedbolus">Indstiller forlænget bolus</string>
|
||||||
|
<string name="stoppingextendedbolus">Stopper forlænget bolus</string>
|
||||||
|
<string name="updatingbasalrates">Opdaterer basal rater</string>
|
||||||
|
<string name="description_pump_diaconn_g8">Pumpeintegration til Diaconn G8 Pumper</string>
|
||||||
|
<string name="diaconn_g8_pump">Diaconn G8</string>
|
||||||
|
<string name="diaconn_g8_pump_shortname">Diaconn G8</string>
|
||||||
|
<string name="maxbolusviolation">Max bolus overtrædelse</string>
|
||||||
|
<string name="commanderror">Kommando fejl</string>
|
||||||
|
<string name="speederror">Hastighedsfejl</string>
|
||||||
|
<string name="insulinlimitviolation">Overtrædelse af insulingrænse</string>
|
||||||
|
<string name="boluserrorcode">Ønsket: %1$.2fU Afgivet: %2$.2fU Fejlkode: %3$s</string>
|
||||||
|
<string name="diaconn_g8_valuenotsetproperly">Værdi ikke angivet korrekt</string>
|
||||||
|
<string name="diaconn_g8_bt_name_title">Diaconn G8 Bluetooth enhed</string>
|
||||||
|
<string name="diaconn_g8_password_title">Pumpe kodeord</string>
|
||||||
|
<string name="bolusspeed">Bolus hastighed</string>
|
||||||
|
<string name="selectedpump">Valgt pumpe</string>
|
||||||
|
<string name="diaconn_g8_useextended_title">Brug forlængede bolusser til >200%%</string>
|
||||||
|
<string name="diaconn_g8_visualizeextendedaspercentage_title">Vis forlænget bolus som %%</string>
|
||||||
|
<string name="diaconn_g8_bluetooth_status">Bluetooth Status</string>
|
||||||
|
<string name="diagonn_g8_tdd_label">TDD</string>
|
||||||
|
<string name="bolus_step">Bolus Trin</string>
|
||||||
|
<string name="basal_step">Basal Trin</string>
|
||||||
|
<string name="pump_firmware_label">Firmware</string>
|
||||||
|
<string name="diagonn_g8_useroptions">BRUGERINDSTILLINGER</string>
|
||||||
|
<string name="pairfirst">Par venligst din pumpe med din telefon!</string>
|
||||||
|
<string name="processinghistory">"Behandler begivenhed "</string>
|
||||||
|
<string name="apslastLogNum">aps_last_log_num</string>
|
||||||
|
<string name="apsWrappingCount">aps_wrapping_count</string>
|
||||||
|
<string name="diaconn_g8_history_tempbasal">Midlertidig basal</string>
|
||||||
|
<string name="diaconng8_pump_settings">Diaconn pumpe indstillinger</string>
|
||||||
|
<string name="diaconn_g8_pumpalarm">Lyd</string>
|
||||||
|
<string name="diaconn_g8_pumpalarm_sound">lyd</string>
|
||||||
|
<string name="diaconn_g8_pumpalarm_vibrate">vibrér</string>
|
||||||
|
<string name="diaconn_g8_pumpalarm_silent">lydløs</string>
|
||||||
|
<string name="diaconn_g8_pumpalarmlevel">Alarm intensitet</string>
|
||||||
<string name="diaconn_g8_pumpalarm_intensity_low">lav</string>
|
<string name="diaconn_g8_pumpalarm_intensity_low">lav</string>
|
||||||
|
<string name="diaconn_g8_pumpalarm_intensity_middle">middel</string>
|
||||||
|
<string name="diaconn_g8_pumpalarm_intensity_high">høj</string>
|
||||||
|
<string name="diaconn_g8_screentimeout">LCD tændt tid [second]</string>
|
||||||
|
<string name="diaconn_g8_saveuseroptions">GEM INDSTILLING TIL PUMPE</string>
|
||||||
|
<string name="diaconn_g8_language">Sprog</string>
|
||||||
|
<string name="diaconn_g8_bolus_speed">Bolus hastighed</string>
|
||||||
|
<string name="diaconn_g8_pumplang_chiness">Kinesisk</string>
|
||||||
|
<string name="diaconn_g8_pumplang_korean">Koreansk</string>
|
||||||
|
<string name="diaconn_g8_pumplang_english">Engelsk</string>
|
||||||
|
<string name="diaconn_g8_screentimeout_10">"10 "</string>
|
||||||
|
<string name="diaconn_g8_pumpscreentimeout_10">10</string>
|
||||||
|
<string name="diaconn_g8_pumpscreentimeout_20">20</string>
|
||||||
|
<string name="diaconn_g8_pumpscreentimeout_30">30</string>
|
||||||
<string name="diaconn_g8_pumpalarm_low">lav</string>
|
<string name="diaconn_g8_pumpalarm_low">lav</string>
|
||||||
|
<string name="injectionblocked">Injektion blokkeret</string>
|
||||||
|
<string name="batterywarning">Batteri Advarsel</string>
|
||||||
|
<string name="insulinlackwarning">Lavt insulin niveau</string>
|
||||||
|
<string name="needbatteryreplace">Batteriudskiftning er påkrævet</string>
|
||||||
|
<string name="needinsullinreplace">Insulin udskiftning er påkrævet</string>
|
||||||
|
<string name="pumpversion">pump_version</string>
|
||||||
|
<string name="apsIncarnationNo">aps_incarnation_no</string>
|
||||||
|
<string name="pumpserialno">pump_serial_no</string>
|
||||||
|
<string name="diaconn_g8_loginsulinchange_title">Log reservoir skift</string>
|
||||||
|
<string name="diaconn_g8_loginsulinchange_summary">Tilføj \"Insulin Skift\" begivenhed til careportal, når de registreres i historik</string>
|
||||||
|
<string name="diaconn_g8_logcanulachange_title">Log indstik skift</string>
|
||||||
|
<string name="diaconn_g8_logcanulachange_summary">Tilføj \"Indstik skift\" begivenhed til careportal, når de registreres i historik</string>
|
||||||
|
<string name="diaconn_g8_logbatterychange_summary">Tilføj \"Batteri skift\" begivenhed til careportal, når det registreres i historik</string>
|
||||||
|
<string name="diaconn_g8_logbatterychange_title">Log skift af batteri</string>
|
||||||
|
<string name="diaconn_g8_logsyncinprogress">Synkronisering af log i gang</string>
|
||||||
|
<string name="diaconn_g8_loginsulinshorage">Lavt insulinniveau</string>
|
||||||
|
<string name="diaconn_g8_logbatteryshorage">Lavt batteriniveau</string>
|
||||||
|
<string name="diaconn_g8_logneedleprime">Nåle fyldning: %1$.2fIE</string>
|
||||||
|
<string name="diaconn_g8_loginjectorprime">Forfyldning: %1$.2fIE</string>
|
||||||
|
<string name="diaconn_g8_logtubeprime">Slange fyldning: %1$.2fIE</string>
|
||||||
|
<string name="diaconn_g8_resetfactoryreset">Nulstil efter fabriksnulstilling</string>
|
||||||
|
<string name="diaconn_g8_resetemergencyoff">Nulstil efter nødstilstand</string>
|
||||||
|
<string name="diaconn_g8_resetbatteryreplacement">Nulstil efter udskiftning af batteri</string>
|
||||||
|
<string name="diaconn_g8_resetaftercalibration">Nulstil efter kalibrering</string>
|
||||||
|
<string name="diaconn_g8_resetpreshipment">Nulstil til fabriksindstillinger</string>
|
||||||
|
<string name="diaconn_g8_resetunexpected">Uventet systemnulstilling</string>
|
||||||
|
<string name="diaconn_g8_reasoncomplete">Fuldført</string>
|
||||||
|
<string name="diaconn_g8_reasoninjectonblock">Injektion blokkeret</string>
|
||||||
|
<string name="diaconn_g8_reasonbatteryshortage">Lavt batteriniveau</string>
|
||||||
|
<string name="diaconn_g8_reasoninsulinshortage">Lavt insulinniveau</string>
|
||||||
|
<string name="diaconn_g8_reasonuserstop">Stoppet af bruger</string>
|
||||||
|
<string name="diaconn_g8_reasonsystemreset">Nulstil system</string>
|
||||||
<string name="diaconn_g8_reasonother">Andet</string>
|
<string name="diaconn_g8_reasonother">Andet</string>
|
||||||
|
<string name="diaconn_g8_reasonemergencystop">Nødstop</string>
|
||||||
|
<string name="diacon_g8_blockbasal">BASAL</string>
|
||||||
|
<string name="diacon_g8_blockmealbolus">MÅLTIDS BOLUS</string>
|
||||||
|
<string name="diacon_g8_blocknormalbolus">NORMAL BOLUS</string>
|
||||||
|
<string name="diacon_g8_blocksquarebolus">FORLÆNGET BOLUS</string>
|
||||||
|
<string name="diacon_g8_blockdualbolus">KOMBINERET BOLUS</string>
|
||||||
|
<string name="diacon_g8_blockreplacetube">UDSKIFT SLANGE</string>
|
||||||
|
<string name="diacon_g8_blockreplaceneedle">UDSKIFT KANYLE</string>
|
||||||
|
<string name="diacon_g8_blockreplacesyringe">UDSKIFT SPRØJTE</string>
|
||||||
|
<string name="diaconn_g8_logalarmblock">Injektion blokkeret (%s)</string>
|
||||||
|
<string name="diaconn_g8_lgorelease">basal afgivelse (%s)</string>
|
||||||
|
<string name="diaconn_g8_lgosuspend">basal suspendering (%s)</string>
|
||||||
|
<string name="diaconn_g8_logdualnormalsuccess">Normal kombinationsbolus vellykket</string>
|
||||||
|
<string name="diaconn_g8_logdualsquarestart">Kombineret bolus startet</string>
|
||||||
|
<string name="diaconn_g8_logdualsquaresuccess">Kombineret bolus vellykket</string>
|
||||||
|
<string name="diaconn_g8_logsquarestart">Forlænget bolus startet</string>
|
||||||
|
<string name="diaconn_g8_logsquaresuccess">Forlænget bolus vellykket</string>
|
||||||
|
<string name="diaconn_g8_logmealfail">Måltid Mislykkedes</string>
|
||||||
|
<string name="diaconn_g8_logsuccess">Succes</string>
|
||||||
|
<string name="diaconn_g8_logmealsuccess">Måltid Gennemført</string>
|
||||||
|
<string name="diaconn_g8_errorcode_1">Kan ikke kontrollere pga. pakke CRC fejl.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_2">Kan ikke indstilles pga. en inputparameter fejl.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_3">Kan ikke indstilles pga. en protokol specifikationsfejl.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_4">Måltid registreret, kan ikke afgive.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_6">Annulleret af pumpen</string>
|
||||||
|
<string name="diaconn_g8_errorcode_7">Tager andre handlinger, begrænser app-indstillinger.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_8">Under en anden Bolus-injektion er injektioner begrænsede.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_9">Kræver genoptagelse af basal afgivelse</string>
|
||||||
|
<string name="diaconn_g8_errorcode_10">Annulleret pga. manglende respons på pumpen.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_11">Injektion er ikke mulig pga. lavt batteri.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_12">Insulinmangel, kan ikke afgives.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_13">Du kan ikke afgive, da det overskrider grænsen.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_14">Injektionen kan ikke afgives, da den overstiger max. daglig dosis.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_15">Efter basal opsætning er færdig, kan basal injektion udføres.</string>
|
||||||
|
<string name="diaconn_g8_errotpreceivedyet">Kommandoen blev ikke leveret. Prøv igen.</string>
|
||||||
|
<string name="diaconn_g8_logtubechange_title">Log skift af slange</string>
|
||||||
|
<string name="diaconn_g8_logtubechange_summary">Tilføj \"Slange skift\" begivenhed til careportal, når de registreres i historik</string>
|
||||||
|
<string name="diaconn_g8_logtempstart">Midlertidig basal start</string>
|
||||||
|
<string name="diaconn_g8_errorcode_32">Under LGS er injektionen begrænset</string>
|
||||||
|
<string name="diaconn_g8_errorcode_33">LGS-status er allerede TIL, kommando afvist.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_34">LGS-status er allerede FRA, kommando afvist.</string>
|
||||||
|
<string name="diaconn_g8_errorcode_35">Midlertidig basal kan ikke startes fordi en anden midlertidig basal er i gang</string>
|
||||||
|
<string name="diaconn_g8_errorcode_36">Midlertidig basal kan ikke stoppes fordi en midlertidig basal ikke er i gang</string>
|
||||||
|
<string name="diaconn_g8_cloudsend_summary">Send pumpelogs til Diaconn Cloud.</string>
|
||||||
|
<string name="diaconn_g8_cloudsend_title">Diaconn Cloud Synkronisering</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -152,4 +152,6 @@
|
||||||
<string name="diaconn_g8_errorcode_34">LGS status er AV. AV kommandoen er avslått.</string>
|
<string name="diaconn_g8_errorcode_34">LGS status er AV. AV kommandoen er avslått.</string>
|
||||||
<string name="diaconn_g8_errorcode_35">Temp basal avslått siden det allerede kjøres en temp basal</string>
|
<string name="diaconn_g8_errorcode_35">Temp basal avslått siden det allerede kjøres en temp basal</string>
|
||||||
<string name="diaconn_g8_errorcode_36">Stopp av temp basal avslått siden det ikke kjøres en temp basal</string>
|
<string name="diaconn_g8_errorcode_36">Stopp av temp basal avslått siden det ikke kjøres en temp basal</string>
|
||||||
|
<string name="diaconn_g8_cloudsend_summary">Send pumpe logger til Diaconn Cloud.</string>
|
||||||
|
<string name="diaconn_g8_cloudsend_title">Diaconn Cloud Sync</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl = https\://services.gradle.org/distributions/gradle-7.2-all.zip
|
distributionUrl = https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||||
|
|
|
@ -91,4 +91,6 @@
|
||||||
<string name="set_neutral_temps_summary">Hvis aktiveret, vil det annullere en midlertidig basal inden udgangen af hver time. Denne metode kan hjælpe med at stoppe nogle pumper i at bippe/vibrere hver time.</string>
|
<string name="set_neutral_temps_summary">Hvis aktiveret, vil det annullere en midlertidig basal inden udgangen af hver time. Denne metode kan hjælpe med at stoppe nogle pumper i at bippe/vibrere hver time.</string>
|
||||||
<string name="mdt_tbr_remaining">%1$.1f E/t (%2$d min. tilbage)</string>
|
<string name="mdt_tbr_remaining">%1$.1f E/t (%2$d min. tilbage)</string>
|
||||||
<string name="invalid_history_data">Ugyldig pumpehistorikdata registreret. Åbn nyt problem og upload logfiler.</string>
|
<string name="invalid_history_data">Ugyldig pumpehistorikdata registreret. Åbn nyt problem og upload logfiler.</string>
|
||||||
|
<string name="riley_statistics">RL Statistik</string>
|
||||||
|
<string name="medtronic_history_type">Type:</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -77,4 +77,5 @@
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="orange_use_scanning_level">Brug Scanning</string>
|
<string name="orange_use_scanning_level">Brug Scanning</string>
|
||||||
<string name="orange_use_scanning_level_summary">Scan før du opretter forbindelse til OrangeLink, det bør forbedre forbindelsen (kan også bruges med andre RileyLink kloner, hvis nødvendigt)</string>
|
<string name="orange_use_scanning_level_summary">Scan før du opretter forbindelse til OrangeLink, det bør forbedre forbindelsen (kan også bruges med andre RileyLink kloner, hvis nødvendigt)</string>
|
||||||
|
<string name="rileylink_configuration">RileyLink Konfiguration</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -5,6 +5,7 @@ enum class LTag(val tag: String, val defaultValue : Boolean = true, val requires
|
||||||
APS("APS"),
|
APS("APS"),
|
||||||
AUTOSENS("AUTOSENS", defaultValue = false),
|
AUTOSENS("AUTOSENS", defaultValue = false),
|
||||||
AUTOMATION("AUTOMATION"),
|
AUTOMATION("AUTOMATION"),
|
||||||
|
AUTOTUNE("AUTOTUNE", defaultValue = false),
|
||||||
BGSOURCE("BGSOURCE"),
|
BGSOURCE("BGSOURCE"),
|
||||||
CONFIGBUILDER("CONFIGBUILDER"),
|
CONFIGBUILDER("CONFIGBUILDER"),
|
||||||
CONSTRAINTS("CONSTRAINTS"),
|
CONSTRAINTS("CONSTRAINTS"),
|
||||||
|
|
|
@ -2,6 +2,18 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">AAPS</string>
|
<string name="app_name">AAPS</string>
|
||||||
<string name="label_actions_activity">AAPS</string>
|
<string name="label_actions_activity">AAPS</string>
|
||||||
|
<string name="label_watchface">AAPS</string>
|
||||||
|
<string name="label_watchface_large">AAPS (Stor)</string>
|
||||||
|
<string name="label_watchface_big_chart">AAPS(StorGraf)</string>
|
||||||
|
<string name="label_watchface_no_chart">AAPS(IngenGraf)</string>
|
||||||
|
<string name="label_watchface_circle">AAPS(Cirkel)</string>
|
||||||
|
<string name="label_watchface_v2">AAPS(v2)</string>
|
||||||
|
<string name="label_watchface_cockpit">AAPS(Cockpit)</string>
|
||||||
|
<string name="label_watchface_steampunk">AAPS(Steampunk)</string>
|
||||||
|
<string name="label_watchface_digital_style">AAPS (DigitalStil)</string>
|
||||||
|
<string name="label_actions_tile">AAPS (Handlinger)</string>
|
||||||
|
<string name="label_temp_target_tile">AAPS(Midl. Mål)</string>
|
||||||
|
<string name="label_quick_wizard_tile">AAPS(Hurtig Guide)</string>
|
||||||
<string name="label_warning_sync">Ingen data!</string>
|
<string name="label_warning_sync">Ingen data!</string>
|
||||||
<string name="label_warning_old">Gammel data!</string>
|
<string name="label_warning_old">Gammel data!</string>
|
||||||
<string name="label_warning_since">Siden %1$s</string>
|
<string name="label_warning_since">Siden %1$s</string>
|
||||||
|
@ -45,14 +57,23 @@
|
||||||
<string name="pref_ring_history">Ring Historik</string>
|
<string name="pref_ring_history">Ring Historik</string>
|
||||||
<string name="pref_light_ring_history">Lys Ring Historik</string>
|
<string name="pref_light_ring_history">Lys Ring Historik</string>
|
||||||
<string name="pref_animations">Animationer</string>
|
<string name="pref_animations">Animationer</string>
|
||||||
|
<string name="pref_wizard_in_menu">Beregner i Menu</string>
|
||||||
<string name="pref_prime_in_menu">Prime i menuen</string>
|
<string name="pref_prime_in_menu">Prime i menuen</string>
|
||||||
<string name="pref_single_target">Enkelt Mål</string>
|
<string name="pref_single_target">Enkelt Mål</string>
|
||||||
|
<string name="pref_wizard_percentage">Beregner Procent</string>
|
||||||
<string name="pref_complication_tap_action">Complication handling ved tryk</string>
|
<string name="pref_complication_tap_action">Complication handling ved tryk</string>
|
||||||
<string name="pref_unicode_in_complications">Unicode i komplikationer</string>
|
<string name="pref_unicode_in_complications">Unicode i komplikationer</string>
|
||||||
<string name="pref_version">Version:</string>
|
<string name="pref_version">Version:</string>
|
||||||
|
<string name="pref_moreWatchfaceSettings">Flere urskive indstillinger</string>
|
||||||
<string name="pref_lookInYourWatchfaceConfiguration">Se venligst på Watchface-konfigurationen.</string>
|
<string name="pref_lookInYourWatchfaceConfiguration">Se venligst på Watchface-konfigurationen.</string>
|
||||||
<string name="menu_tempt">MidlertidigMål</string>
|
<string name="menu_tempt">MidlertidigMål</string>
|
||||||
|
<string name="menu_wizard">Beregner</string>
|
||||||
|
<string name="menu_wizard_short">Beregn</string>
|
||||||
|
<string name="menu_treatment">Behandling</string>
|
||||||
|
<string name="menu_treatment_short">Behandl</string>
|
||||||
<string name="menu_bolus">Bolus</string>
|
<string name="menu_bolus">Bolus</string>
|
||||||
|
<string name="menu_carb">Kulhydrater</string>
|
||||||
|
<string name="menu_ecarb">Forlængede kulhydrater</string>
|
||||||
<string name="menu_settings">Indstillinger</string>
|
<string name="menu_settings">Indstillinger</string>
|
||||||
<string name="menu_status">Status</string>
|
<string name="menu_status">Status</string>
|
||||||
<string name="menu_resync">Gensynkronisering</string>
|
<string name="menu_resync">Gensynkronisering</string>
|
||||||
|
@ -60,17 +81,38 @@
|
||||||
<string name="menu_none">Ingen</string>
|
<string name="menu_none">Ingen</string>
|
||||||
<string name="menu_default">Standard</string>
|
<string name="menu_default">Standard</string>
|
||||||
<string name="menu_menu">Menu</string>
|
<string name="menu_menu">Menu</string>
|
||||||
|
<string name="quick_wizard_short">XL</string>
|
||||||
|
<string name="action_duration">Varighed</string>
|
||||||
|
<string name="action_tempt_confirmation">Midlertidig Mål Anmodet</string>
|
||||||
|
<string name="action_quick_wizard_confirmation">Hurtig Guide Anmodet</string>
|
||||||
|
<string name="action_treatment_confirmation">Behandling Anmodet</string>
|
||||||
|
<string name="action_bolus_confirmation">Bolus Anmodet</string>
|
||||||
|
<string name="action_wizard_confirmation">Beregning Anmodet</string>
|
||||||
|
<string name="action_fill_confirmation">Fyldning Anmodet</string>
|
||||||
|
<string name="action_ecarb_confirmation">Kulhydrater Anmodet</string>
|
||||||
|
<string name="action_profile_switch_confirmation">Profilskift anmodet</string>
|
||||||
|
<string name="action_target" comment="In temp target menu, single target value">Mål</string>
|
||||||
|
<string name="action_low" comment="In temp target menu, lower value from range">Lav</string>
|
||||||
|
<string name="action_high" comment="In temp target menu, higher value from range">Høj</string>
|
||||||
|
<string name="action_carbs">Kulhydrater</string>
|
||||||
|
<string name="action_ecarbs">Forlængede kulhydrater</string>
|
||||||
|
<string name="action_percentage">Procentdel</string>
|
||||||
|
<string name="action_start_min">Start [min]</string>
|
||||||
|
<string name="action_duration_h">Varighed [h]</string>
|
||||||
|
<string name="action_insulin">Insulin</string>
|
||||||
<string name="action_preset_1">Forvalg 1</string>
|
<string name="action_preset_1">Forvalg 1</string>
|
||||||
<string name="action_preset_2">Forvalg 2</string>
|
<string name="action_preset_2">Forvalg 2</string>
|
||||||
<string name="action_preset_3">Forvalg 3</string>
|
<string name="action_preset_3">Forvalg 3</string>
|
||||||
<string name="action_free_amount" comment="In prime/fill menu, allows to enter any amount to be used for priming/filling">Fri mængde</string>
|
<string name="action_free_amount" comment="In prime/fill menu, allows to enter any amount to be used for priming/filling">Fri mængde</string>
|
||||||
<string name="action_confirm">BEKRÆFT</string>
|
<string name="action_confirm">BEKRÆFT</string>
|
||||||
<string name="action_timeshift">tidsforskydning</string>
|
<string name="action_timeshift">tidsforskydning</string>
|
||||||
|
<string name="action_bolus">Bolus</string>
|
||||||
<string name="bolus_progress">Bolus Fremskridt</string>
|
<string name="bolus_progress">Bolus Fremskridt</string>
|
||||||
<string name="press_to_cancel">tryk for at annullere</string>
|
<string name="press_to_cancel">tryk for at annullere</string>
|
||||||
<string name="cancel_bolus">ANNULLER BOLUS</string>
|
<string name="cancel_bolus">ANNULLER BOLUS</string>
|
||||||
<string name="status_pump">Pumpe</string>
|
<string name="status_pump">Pumpe</string>
|
||||||
<string name="status_loop">Loop</string>
|
<string name="status_loop">Loop</string>
|
||||||
|
<string name="status_profile_switch">Profilskift</string>
|
||||||
<string name="status_tdd">TDD</string>
|
<string name="status_tdd">TDD</string>
|
||||||
<string name="activity_carb">Kulhydrat</string>
|
<string name="activity_carb">Kulhydrat</string>
|
||||||
<string name="activity_IOB">IOB</string>
|
<string name="activity_IOB">IOB</string>
|
||||||
|
@ -117,4 +159,13 @@
|
||||||
<string name="simple_ui_charging">Under Opladning</string>
|
<string name="simple_ui_charging">Under Opladning</string>
|
||||||
<string name="simple_ui_always_on">Altid Tændt Tilstand</string>
|
<string name="simple_ui_always_on">Altid Tændt Tilstand</string>
|
||||||
<string name="simple_ui_always_on_charging">Altid Tændt ved opladning</string>
|
<string name="simple_ui_always_on_charging">Altid Tændt ved opladning</string>
|
||||||
|
<string name="temp_target_eating_soon">Spiser</string>
|
||||||
|
<string name="temp_target_hypo">Hypo</string>
|
||||||
|
<string name="temp_target_activity">Aktivitet</string>
|
||||||
|
<string name="temp_target_manual">Manuel</string>
|
||||||
|
<string name="temp_target_cancel">Annuller</string>
|
||||||
|
<string name="tile_none">Ingen</string>
|
||||||
|
<string name="tile_no_config">Ingen konfiguration tilgængelig</string>
|
||||||
|
<string name="wear_control_not_enabled">Wear kontrol deaktiveret</string>
|
||||||
|
<string name="wear_control_no_data">Ingen data tilgængelig</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2,6 +2,15 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">AAPS</string>
|
<string name="app_name">AAPS</string>
|
||||||
<string name="label_actions_activity">AAPS</string>
|
<string name="label_actions_activity">AAPS</string>
|
||||||
|
<string name="label_watchface">AAPS</string>
|
||||||
|
<string name="label_watchface_large">AAPS (stor)</string>
|
||||||
|
<string name="label_watchface_big_chart">AAPS (stor graf)</string>
|
||||||
|
<string name="label_watchface_no_chart">AAPS (ingen graf)</string>
|
||||||
|
<string name="label_watchface_circle">AAPS (sirkel)</string>
|
||||||
|
<string name="label_watchface_v2">AAPS(v2)</string>
|
||||||
|
<string name="label_watchface_cockpit">AAPS(Cockpit)</string>
|
||||||
|
<string name="label_watchface_steampunk">AAPS(Steampunk)</string>
|
||||||
|
<string name="label_watchface_digital_style">AAPS(Digitalstil)</string>
|
||||||
<string name="label_actions_tile">AAPS(Actions)</string>
|
<string name="label_actions_tile">AAPS(Actions)</string>
|
||||||
<string name="label_temp_target_tile">AAPS(Temp Target)</string>
|
<string name="label_temp_target_tile">AAPS(Temp Target)</string>
|
||||||
<string name="label_quick_wizard_tile">AAPS(Quick Wizard)</string>
|
<string name="label_quick_wizard_tile">AAPS(Quick Wizard)</string>
|
||||||
|
@ -55,6 +64,7 @@
|
||||||
<string name="pref_complication_tap_action">Komplikasjon trykk handling</string>
|
<string name="pref_complication_tap_action">Komplikasjon trykk handling</string>
|
||||||
<string name="pref_unicode_in_complications">Unicode ikomplikasjoner</string>
|
<string name="pref_unicode_in_complications">Unicode ikomplikasjoner</string>
|
||||||
<string name="pref_version">Versjon:</string>
|
<string name="pref_version">Versjon:</string>
|
||||||
|
<string name="pref_moreWatchfaceSettings">Fler urskive innstillinger</string>
|
||||||
<string name="pref_lookInYourWatchfaceConfiguration">Vennligst sjekk urskive innstillinger.</string>
|
<string name="pref_lookInYourWatchfaceConfiguration">Vennligst sjekk urskive innstillinger.</string>
|
||||||
<string name="menu_tempt">TempT</string>
|
<string name="menu_tempt">TempT</string>
|
||||||
<string name="menu_wizard">Kalkulator</string>
|
<string name="menu_wizard">Kalkulator</string>
|
||||||
|
@ -80,6 +90,7 @@
|
||||||
<string name="action_wizard_confirmation">Beregning forespurt</string>
|
<string name="action_wizard_confirmation">Beregning forespurt</string>
|
||||||
<string name="action_fill_confirmation">Fylling forespurt</string>
|
<string name="action_fill_confirmation">Fylling forespurt</string>
|
||||||
<string name="action_ecarb_confirmation">Karbo forespurt</string>
|
<string name="action_ecarb_confirmation">Karbo forespurt</string>
|
||||||
|
<string name="action_profile_switch_confirmation">Profilbytte forespurt</string>
|
||||||
<string name="action_target" comment="In temp target menu, single target value">Mål</string>
|
<string name="action_target" comment="In temp target menu, single target value">Mål</string>
|
||||||
<string name="action_low" comment="In temp target menu, lower value from range">Lav</string>
|
<string name="action_low" comment="In temp target menu, lower value from range">Lav</string>
|
||||||
<string name="action_high" comment="In temp target menu, higher value from range">Høy</string>
|
<string name="action_high" comment="In temp target menu, higher value from range">Høy</string>
|
||||||
|
@ -101,6 +112,7 @@
|
||||||
<string name="cancel_bolus">AVBRYT BOLUS</string>
|
<string name="cancel_bolus">AVBRYT BOLUS</string>
|
||||||
<string name="status_pump">Pumpe</string>
|
<string name="status_pump">Pumpe</string>
|
||||||
<string name="status_loop">Loop</string>
|
<string name="status_loop">Loop</string>
|
||||||
|
<string name="status_profile_switch">Profilbytte</string>
|
||||||
<string name="status_tdd">TDD</string>
|
<string name="status_tdd">TDD</string>
|
||||||
<string name="activity_carb">Karbo</string>
|
<string name="activity_carb">Karbo</string>
|
||||||
<string name="activity_IOB">IOB</string>
|
<string name="activity_IOB">IOB</string>
|
||||||
|
|
Loading…
Reference in a new issue