DeviceStatus

This commit is contained in:
Milos Kozak 2021-03-31 21:54:49 +02:00
parent 93b1b37021
commit a5ea0d9027
18 changed files with 3324 additions and 206 deletions

View file

@ -17,6 +17,7 @@ abstract class ReceiversModule {
@ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver @ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver
@ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver @ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver @ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveReceiver.KeepAliveWorker
@ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver @ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver
@ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver @ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver
@ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver @ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver

View file

@ -32,6 +32,7 @@ import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
@ -48,6 +49,7 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.extensions.buildDeviceStatus
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -78,7 +80,8 @@ open class LoopPlugin @Inject constructor(
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val nsUpload: NSUpload, private val nsUpload: NSUpload,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val repository: AppRepository private val repository: AppRepository,
private val runningConfiguration: RunningConfiguration
) : PluginBase(PluginDescription() ) : PluginBase(PluginDescription()
.mainType(PluginType.LOOP) .mainType(PluginType.LOOP)
.fragmentClass(LoopFragment::class.java.name) .fragmentClass(LoopFragment::class.java.name)
@ -316,7 +319,12 @@ open class LoopPlugin @Inject constructor(
lastRun!!.lastTBRRequest = 0 lastRun!!.lastTBRRequest = 0
lastRun!!.lastSMBEnact = 0 lastRun!!.lastSMBEnact = 0
lastRun!!.lastSMBRequest = 0 lastRun!!.lastSMBRequest = 0
nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION) buildDeviceStatus(dateUtil, this, iobCobCalculatorPlugin, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
if (isSuspended) { if (isSuspended) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended)) aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended))) rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)))
@ -489,7 +497,6 @@ open class LoopPlugin @Inject constructor(
fun acceptChangeRequest() { fun acceptChangeRequest() {
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
val lp = this
applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() { applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() {
override fun run() { override fun run() {
if (result.enacted) { if (result.enacted) {
@ -497,7 +504,11 @@ open class LoopPlugin @Inject constructor(
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
lastRun!!.lastTBREnact = DateUtil.now() lastRun!!.lastTBREnact = DateUtil.now()
lastRun!!.lastOpenModeAccept = DateUtil.now() lastRun!!.lastOpenModeAccept = DateUtil.now()
nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION) buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculatorPlugin, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
sp.incInt(R.string.key_ObjectivesmanualEnacts) sp.incInt(R.string.key_ObjectivesmanualEnacts)
} }
rxBus.send(EventAcceptOpenLoopChange()) rxBus.send(EventAcceptOpenLoopChange())

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.general.nsclient
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.entities.* import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.DataSyncSelector import info.nightscout.androidaps.interfaces.DataSyncSelector
@ -28,6 +29,7 @@ class DataSyncSelectorImplementation @Inject constructor(
sp.remove(R.string.key_ns_bolus_last_synced_id) sp.remove(R.string.key_ns_bolus_last_synced_id)
sp.remove(R.string.key_ns_carbs_last_synced_id) sp.remove(R.string.key_ns_carbs_last_synced_id)
sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id) sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id)
sp.remove(R.string.key_ns_device_status_last_synced_id)
} }
override fun confirmLastBolusIdIfGreater(lastSynced: Long) { override fun confirmLastBolusIdIfGreater(lastSynced: Long) {
@ -294,4 +296,34 @@ class DataSyncSelectorImplementation @Inject constructor(
return false return false
} }
override fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced)
}
}
override fun changedDeviceStatuses(): List<DeviceStatus> {
val startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
return appRepository.getModifiedDeviceStatusDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading DeviceStatus data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedDeviceStatusesCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
aapsLogger.info(LTag.DATABASE, "Loading DeviceStatus data Start: $startId ID: ${deviceStatus.id}")
when {
// without nsId = create new
deviceStatus.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("devicestatus", deviceStatus.toJson(), deviceStatus)
// with nsId = ignore
deviceStatus.interfaceIDs.nightscoutId != null -> Any()
}
return true
}
return false
}
} }

View file

@ -32,10 +32,11 @@ import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.R; import info.nightscout.androidaps.R;
import info.nightscout.androidaps.database.AppRepository; import info.nightscout.androidaps.database.AppRepository;
import info.nightscout.androidaps.database.entities.Carbs; import info.nightscout.androidaps.database.entities.DeviceStatus;
import info.nightscout.androidaps.database.transactions.UpdateNsIdBolusCalculatorResultTransaction; import info.nightscout.androidaps.database.transactions.UpdateNsIdBolusCalculatorResultTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdBolusTransaction; import info.nightscout.androidaps.database.transactions.UpdateNsIdBolusTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdCarbsTransaction; import info.nightscout.androidaps.database.transactions.UpdateNsIdCarbsTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdDeviceStatusTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdFoodTransaction; import info.nightscout.androidaps.database.transactions.UpdateNsIdFoodTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdGlucoseValueTransaction; import info.nightscout.androidaps.database.transactions.UpdateNsIdGlucoseValueTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdTemporaryTargetTransaction; import info.nightscout.androidaps.database.transactions.UpdateNsIdTemporaryTargetTransaction;
@ -339,6 +340,22 @@ public class NSClientService extends DaggerService {
dataSyncSelector.processChangedBolusCalculatorResultsCompat(); dataSyncSelector.processChangedBolusCalculatorResultsCompat();
return; return;
} }
if (ack.getOriginalObject() instanceof DeviceStatus) {
DeviceStatus deviceStatus = (DeviceStatus) ack.getOriginalObject();
deviceStatus.getInterfaceIDs().setNightscoutId(ack.getId());
disposable.add(repository.runTransactionForResult(new UpdateNsIdDeviceStatusTransaction(deviceStatus))
.observeOn(aapsSchedulers.getIo())
.subscribe(
result -> aapsLogger.debug(LTag.DATABASE, "Updated ns id of DeviceStatus " + deviceStatus),
error -> aapsLogger.error(LTag.DATABASE, "Updated ns id of DeviceStatus failed", error)
));
dataSyncSelector.confirmLastDeviceStatusIdIfGreater(deviceStatus.getId());
rxBus.send(new EventNSClientNewLog("DBADD", "Acked DeviceStatus" + deviceStatus.getInterfaceIDs().getNightscoutId()));
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResultsCompat();
return;
}
// old way // old way
if (ack.nsClientID != null) { if (ack.nsClientID != null) {
uploadQueue.removeByNsClientIdIfExists(ack.json); uploadQueue.removeByNsClientIdIfExists(ack.json);
@ -977,6 +994,7 @@ public class NSClientService extends DaggerService {
dataSyncSelector.processChangedTempTargetsCompat(); dataSyncSelector.processChangedTempTargetsCompat();
dataSyncSelector.processChangedFoodsCompat(); dataSyncSelector.processChangedFoodsCompat();
dataSyncSelector.processChangedTherapyEventsCompat(); dataSyncSelector.processChangedTherapyEventsCompat();
dataSyncSelector.processChangedDeviceStatusesCompat();
if (uploadQueue.size() == 0) if (uploadQueue.size() == 0)
return; return;

View file

@ -3,13 +3,19 @@ package info.nightscout.androidaps.receivers
import android.app.AlarmManager import android.app.AlarmManager
import android.app.PendingIntent import android.app.PendingIntent
import android.app.PendingIntent.CanceledException import android.app.PendingIntent.CanceledException
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.PowerManager
import android.os.SystemClock import android.os.SystemClock
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.DaggerBroadcastReceiver import dagger.android.DaggerBroadcastReceiver
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.Config import info.nightscout.androidaps.Config
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.events.EventProfileNeedsUpdate import info.nightscout.androidaps.events.EventProfileNeedsUpdate
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
@ -18,55 +24,124 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.LocalAlertUtils import info.nightscout.androidaps.utils.LocalAlertUtils
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.extensions.buildDeviceStatus
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
class KeepAliveReceiver : DaggerBroadcastReceiver() { class KeepAliveReceiver : DaggerBroadcastReceiver() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var localAlertUtils: LocalAlertUtils
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var receiverStatusStore: ReceiverStatusStore
@Inject lateinit var config: Config
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil
companion object { companion object {
private val KEEP_ALIVE_MILLISECONDS = T.mins(5).msecs() private val KEEP_ALIVE_MILLISECONDS = T.mins(5).msecs()
private val STATUS_UPDATE_FREQUENCY = T.mins(15).msecs()
private val IOB_UPDATE_FREQUENCY_IN_MINS = 5L
private var lastReadStatus: Long = 0
private var lastRun: Long = 0
private var lastIobUpload: Long = 0
} }
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent) super.onReceive(context, intent)
aapsLogger.debug(LTag.CORE, "KeepAlive received") aapsLogger.debug(LTag.CORE, "KeepAlive received")
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:KeepAliveReceiver") WorkManager.getInstance(context)
wl.acquire(T.mins(2).msecs()) .enqueue(OneTimeWorkRequest.Builder(KeepAliveWorker::class.java).build())
localAlertUtils.shortenSnoozeInterval() }
localAlertUtils.checkStaleBGAlert()
checkPump() class KeepAliveWorker(
checkAPS() context: Context,
wl.release() params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var localAlertUtils: LocalAlertUtils
@Inject lateinit var repository: AppRepository
@Inject lateinit var config: Config
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var runningConfiguration: RunningConfiguration
@Inject lateinit var receiverStatusStore: ReceiverStatusStore
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var fabricPrivacy: FabricPrivacy
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
companion object {
private val STATUS_UPDATE_FREQUENCY = T.mins(15).msecs()
private const val IOB_UPDATE_FREQUENCY_IN_MINUTES = 5L
private var lastReadStatus: Long = 0
private var lastRun: Long = 0
private var lastIobUpload: Long = 0
}
override fun doWork(): Result {
localAlertUtils.shortenSnoozeInterval()
localAlertUtils.checkStaleBGAlert()
checkPump()
checkAPS()
return Result.success()
}
// Usually deviceStatus is uploaded through LoopPlugin after every loop cycle.
// if there is no BG available, we have to upload anyway to have correct
// IOB displayed in NS
private fun checkAPS() {
var shouldUploadStatus = false
if (config.NSCLIENT) return
if (config.PUMPCONTROL) shouldUploadStatus = true
else if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null)
shouldUploadStatus = true
else if (DateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true
if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINUTES) && shouldUploadStatus) {
lastIobUpload = dateUtil._now()
buildDeviceStatus(dateUtil, loopPlugin, iobCobCalculatorPlugin, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
}
}
private fun checkPump() {
val pump = activePlugin.activePump
val profile = profileFunction.getProfile() ?: return
val lastConnection = pump.lastDataTime()
val isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis()
val isBasalOutdated = abs(profile.basal - pump.baseBasalRate) > pump.pumpDescription.basalStep
aapsLogger.debug(LTag.CORE, "Last connection: " + dateUtil.dateAndTimeString(lastConnection))
// sometimes keep alive broadcast stops
// as as workaround test if readStatus was requested before an alarm is generated
if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) {
localAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated, loopPlugin.isDisconnected)
}
if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE)) {
rxBus.send(EventProfileNeedsUpdate())
} else if (isStatusOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Status outdated.", null)
} else if (isBasalOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Basal outdated.", null)
}
if (lastRun != 0L && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) {
aapsLogger.error(LTag.CORE, "KeepAlive fail")
fabricPrivacy.logCustom("KeepAliveFail")
}
lastRun = System.currentTimeMillis()
}
} }
class KeepAliveManager @Inject constructor( class KeepAliveManager @Inject constructor(
@ -82,7 +157,7 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() {
localAlertUtils.preSnoozeAlarms() localAlertUtils.preSnoozeAlarms()
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val i = Intent(context, KeepAliveReceiver::class.java) val i = Intent(context, KeepAliveReceiver::class.java)
val pi = PendingIntent.getBroadcast(context, 0, i, 0) val pi = PendingIntent.getBroadcast(context, 0, i, FLAG_IMMUTABLE)
try { try {
pi.send() pi.send()
} catch (e: CanceledException) { } catch (e: CanceledException) {
@ -94,53 +169,9 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() {
fun cancelAlarm(context: Context) { fun cancelAlarm(context: Context) {
aapsLogger.debug(LTag.CORE, "KeepAlive canceled") aapsLogger.debug(LTag.CORE, "KeepAlive canceled")
val intent = Intent(context, KeepAliveReceiver::class.java) val intent = Intent(context, KeepAliveReceiver::class.java)
val sender = PendingIntent.getBroadcast(context, 0, intent, 0) val sender = PendingIntent.getBroadcast(context, 0, intent, FLAG_IMMUTABLE)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.cancel(sender) alarmManager.cancel(sender)
} }
} }
// Usually deviceStatus is uploaded through LoopPlugin after every loop cycle.
// if there is no BG available, we have to upload anyway to have correct
// IOB displayed in NS
private fun checkAPS() {
var shouldUploadStatus = false
if (config.NSCLIENT) return
if (config.PUMPCONTROL) shouldUploadStatus = true
else if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null)
shouldUploadStatus = true
else if (DateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true
if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINS) && shouldUploadStatus) {
lastIobUpload = DateUtil.now()
nsUpload.uploadDeviceStatus(loopPlugin, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
}
}
private fun checkPump() {
val pump = activePlugin.activePump
val profile = profileFunction.getProfile() ?: return
val lastConnection = pump.lastDataTime()
val isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis()
val isBasalOutdated = abs(profile.basal - pump.baseBasalRate) > pump.pumpDescription.basalStep
aapsLogger.debug(LTag.CORE, "Last connection: " + dateUtil.dateAndTimeString(lastConnection))
// sometimes keep alive broadcast stops
// as as workaround test if readStatus was requested before an alarm is generated
if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) {
localAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated, loopPlugin.isDisconnected)
}
if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE)) {
rxBus.send(EventProfileNeedsUpdate())
} else if (isStatusOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Status outdated.", null)
} else if (isBasalOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Basal outdated.", null)
}
if (lastRun != 0L && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) {
aapsLogger.error(LTag.CORE, "KeepAlive fail")
fabricPrivacy.logCustom("KeepAliveFail")
}
lastRun = System.currentTimeMillis()
}
} }

View file

@ -43,6 +43,7 @@
<string name="key_ns_bolus_calculator_result_last_synced_id" translatable="false">ns_bolus_calculator_result_last_synced_id</string> <string name="key_ns_bolus_calculator_result_last_synced_id" translatable="false">ns_bolus_calculator_result_last_synced_id</string>
<string name="key_ns_carbs_last_synced_id" translatable="false">ns_carbs_last_synced_id</string> <string name="key_ns_carbs_last_synced_id" translatable="false">ns_carbs_last_synced_id</string>
<string name="key_ns_bolus_last_synced_id" translatable="false">ns_bolus_last_synced_id</string> <string name="key_ns_bolus_last_synced_id" translatable="false">ns_bolus_last_synced_id</string>
<string name="key_ns_device_status_last_synced_id" translatable="false">ns_device_status_last_synced_id</string>
<string name="treatmentssafety_title">Treatments safety</string> <string name="treatmentssafety_title">Treatments safety</string>
<string name="treatmentssafety_maxbolus_title">Max allowed bolus [U]</string> <string name="treatmentssafety_maxbolus_title">Max allowed bolus [U]</string>

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.interfaces package info.nightscout.androidaps.interfaces
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.entities.* import info.nightscout.androidaps.database.entities.*
interface DataSyncSelector { interface DataSyncSelector {
@ -48,4 +49,9 @@ interface DataSyncSelector {
fun changedFoods() : List<Food> fun changedFoods() : List<Food>
// Until NS v3 // Until NS v3
fun processChangedFoodsCompat(): Boolean fun processChangedFoodsCompat(): Boolean
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long)
fun changedDeviceStatuses() : List<DeviceStatus>
// Until NS v3
fun processChangedDeviceStatusesCompat(): Boolean
} }

View file

@ -1,35 +1,22 @@
package info.nightscout.androidaps.plugins.general.nsclient; package info.nightscout.androidaps.plugins.general.nsclient;
import android.os.Build;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.Date;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import info.nightscout.androidaps.core.R; import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.database.entities.TherapyEvent; import info.nightscout.androidaps.database.entities.TherapyEvent;
import info.nightscout.androidaps.db.DbRequest; import info.nightscout.androidaps.db.DbRequest;
import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.ProfileSwitch;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.interfaces.IobCobCalculator;
import info.nightscout.androidaps.interfaces.LoopInterface;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.UploadQueueInterface; import info.nightscout.androidaps.interfaces.UploadQueueInterface;
import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
import info.nightscout.androidaps.plugins.aps.loop.DeviceStatus;
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration; import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration;
import info.nightscout.androidaps.receivers.ReceiverStatusStore;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.sharedPreferences.SP; import info.nightscout.androidaps.utils.sharedPreferences.SP;
@ -165,70 +152,6 @@ public class NSUpload {
} }
} }
public void uploadDeviceStatus(LoopInterface loopPlugin, IobCobCalculator iobCobCalculatorPlugin, ProfileFunction profileFunction, PumpInterface pumpInterface, ReceiverStatusStore receiverStatusStore, String version) {
Profile profile = profileFunction.getProfile();
String profileName = profileFunction.getProfileName();
if (profile == null) {
aapsLogger.error("Profile is null. Skipping upload");
return;
}
DeviceStatus deviceStatus = new DeviceStatus(aapsLogger);
try {
LoopInterface.LastRun lastRun = loopPlugin.getLastRun();
if (lastRun != null && lastRun.getLastAPSRun() > System.currentTimeMillis() - 300 * 1000L) {
// do not send if result is older than 1 min
APSResult apsResult = lastRun.getRequest();
apsResult.json().put("timestamp", DateUtil.toISOString(lastRun.getLastAPSRun()));
deviceStatus.suggested = apsResult.json();
deviceStatus.iob = lastRun.getRequest().getIob().json();
deviceStatus.iob.put("time", DateUtil.toISOString(lastRun.getLastAPSRun()));
JSONObject requested = new JSONObject();
if (lastRun.getTbrSetByPump() != null && lastRun.getTbrSetByPump().getEnacted()) { // enacted
deviceStatus.enacted = lastRun.getRequest().json();
deviceStatus.enacted.put("rate", lastRun.getTbrSetByPump().json(profile).get("rate"));
deviceStatus.enacted.put("duration", lastRun.getTbrSetByPump().json(profile).get("duration"));
deviceStatus.enacted.put("recieved", true);
requested.put("duration", lastRun.getRequest().getDuration());
requested.put("rate", lastRun.getRequest().getRate());
requested.put("temp", "absolute");
deviceStatus.enacted.put("requested", requested);
}
if (lastRun.getTbrSetByPump() != null && lastRun.getTbrSetByPump().getEnacted()) { // enacted
if (deviceStatus.enacted == null) {
deviceStatus.enacted = lastRun.getRequest().json();
}
deviceStatus.enacted.put("smb", lastRun.getTbrSetByPump().getBolusDelivered());
requested.put("smb", lastRun.getRequest().getSmb());
deviceStatus.enacted.put("requested", requested);
}
} else {
aapsLogger.debug(LTag.NSCLIENT, "OpenAPS data too old to upload, sending iob only");
IobTotal[] iob = iobCobCalculatorPlugin.calculateIobArrayInDia(profile);
if (iob.length > 0) {
deviceStatus.iob = iob[0].json();
deviceStatus.iob.put("time", DateUtil.toISOString(dateUtil._now()));
}
}
deviceStatus.device = "openaps://" + Build.MANUFACTURER + " " + Build.MODEL;
JSONObject pumpstatus = pumpInterface.getJSONStatus(profile, profileName, version);
deviceStatus.pump = pumpstatus;
deviceStatus.uploaderBattery = receiverStatusStore.getBatteryLevel();
deviceStatus.created_at = DateUtil.toISOString(new Date());
deviceStatus.configuration = runningConfiguration.configuration();
uploadQueue.add(new DbRequest("dbAdd", "devicestatus", deviceStatus.mongoRecord(), System.currentTimeMillis()));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void uploadTreatmentRecord(DetailedBolusInfo detailedBolusInfo) { public void uploadTreatmentRecord(DetailedBolusInfo detailedBolusInfo) {
throw new IllegalStateException("DO NOT USE"); throw new IllegalStateException("DO NOT USE");

View file

@ -1,12 +1,88 @@
package info.nightscout.androidaps.plugins.aps.loop; package info.nightscout.androidaps.utils.extensions
import org.json.JSONException; import android.os.Build
import org.json.JSONObject; import info.nightscout.androidaps.database.entities.DeviceStatus
import org.slf4j.Logger; import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.LoopInterface
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpInterface
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.receivers.ReceiverStatusStore
import info.nightscout.androidaps.utils.DateUtil
import org.json.JSONObject
import info.nightscout.androidaps.logging.AAPSLogger; fun DeviceStatus.toJson(): JSONObject =
import info.nightscout.androidaps.logging.LTag; JSONObject()
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; .put("created_at", DateUtil.toISOString(timestamp))
.also {
if (device != null) it.put("device", device)
if (pump != null) it.put("pump", JSONObject(pump))
it.put("openaps", JSONObject().also { openaps ->
if (enacted != null) openaps.put("enacted", JSONObject(enacted))
if (suggested != null) openaps.put("suggested", JSONObject(suggested))
if (iob != null) openaps.put("iob", iob)
})
if (uploaderBattery != 0) it.put("uploaderBattery", uploaderBattery)
if (configuration != null) it.put("configuration", JSONObject(configuration))
}
fun buildDeviceStatus(
dateUtil: DateUtil,
loopPlugin: LoopInterface,
iobCobCalculatorPlugin: IobCobCalculator,
profileFunction: ProfileFunction,
pumpInterface: PumpInterface,
receiverStatusStore: ReceiverStatusStore,
runningConfiguration: RunningConfiguration,
version: String
): DeviceStatus? {
val profile = profileFunction.getProfile() ?: return null
val profileName = profileFunction.getProfileName() ?: return null
val lastRun = loopPlugin.lastRun
var apsResult: JSONObject? = null
var iob: JSONObject? = null
var enacted: JSONObject? = null
if (lastRun != null && lastRun.lastAPSRun > dateUtil._now() - 300 * 1000L) {
// do not send if result is older than 1 min
apsResult = lastRun.request?.json()?.also {
it.put("timestamp", DateUtil.toISOString(lastRun.lastAPSRun))
}
iob = lastRun.request?.iob?.json()?.also {
it.put("time", DateUtil.toISOString(lastRun.lastAPSRun))
}
val requested = JSONObject()
if (lastRun.tbrSetByPump?.enacted == true) { // enacted
enacted = lastRun.request?.json()?.also {
it.put("rate", lastRun.tbrSetByPump!!.json(profile)["rate"])
it.put("duration", lastRun.tbrSetByPump!!.json(profile)["duration"])
it.put("received", true)
}
requested.put("duration", lastRun.request?.duration)
requested.put("rate", lastRun.request?.rate)
requested.put("temp", "absolute")
requested.put("smb", lastRun.request?.smb)
enacted?.put("requested", requested)
enacted?.put("smb", lastRun.tbrSetByPump?.bolusDelivered)
}
} else {
val calcIob = iobCobCalculatorPlugin.calculateIobArrayInDia(profile)
if (calcIob.isNotEmpty()) {
iob = calcIob[0].json()
iob.put("time", DateUtil.toISOString(dateUtil._now()))
}
}
return DeviceStatus(
timestamp = dateUtil._now(),
suggested = apsResult?.toString(),
iob = iob?.toString(),
enacted = enacted?.toString(),
device = "openaps://" + Build.MANUFACTURER + " " + Build.MODEL,
pump = pumpInterface.getJSONStatus(profile, profileName, version).toString(),
uploaderBattery = receiverStatusStore.batteryLevel,
configuration = runningConfiguration.configuration().toString()
)
}
/* /*
{ {
@ -364,41 +440,3 @@ import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
"created_at": "2016-06-24T09:27:49.230Z" "created_at": "2016-06-24T09:27:49.230Z"
} }
*/ */
public class DeviceStatus {
private final AAPSLogger aapsLogger;
public String device = null;
public JSONObject pump = null;
public JSONObject enacted = null;
public JSONObject suggested = null;
public JSONObject iob = null;
public int uploaderBattery = 0;
public String created_at = null;
public JSONObject configuration = null;
public DeviceStatus(AAPSLogger aapsLogger) {
this.aapsLogger = aapsLogger;
}
public JSONObject mongoRecord() {
JSONObject record = new JSONObject();
try {
if (device != null) record.put("device", device);
if (pump != null) record.put("pump", pump);
JSONObject openaps = new JSONObject();
if (enacted != null) openaps.put("enacted", enacted);
if (suggested != null) openaps.put("suggested", suggested);
if (iob != null) openaps.put("iob", iob);
record.put("openaps", openaps);
if (uploaderBattery != 0) record.put("uploaderBattery", uploaderBattery);
if (created_at != null) record.put("created_at", created_at);
if (configuration != null) record.put("configuration", configuration);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
return record;
}
}

File diff suppressed because it is too large Load diff

View file

@ -6,14 +6,14 @@ import androidx.room.TypeConverters
import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.daos.*
import info.nightscout.androidaps.database.entities.* import info.nightscout.androidaps.database.entities.*
const val DATABASE_VERSION = 9 const val DATABASE_VERSION = 10
@Database(version = DATABASE_VERSION, @Database(version = DATABASE_VERSION,
entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class, entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class,
EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class, EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class,
TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class, TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class,
MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class,
Food::class], Food::class, DeviceStatus::class],
exportSchema = true) exportSchema = true)
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
internal abstract class AppDatabase : RoomDatabase() { internal abstract class AppDatabase : RoomDatabase() {
@ -54,4 +54,6 @@ internal abstract class AppDatabase : RoomDatabase() {
abstract val foodDao: FoodDao abstract val foodDao: FoodDao
abstract val deviceStatusDao: DeviceStatusDao
} }

View file

@ -455,6 +455,26 @@ open class AppRepository @Inject internal constructor(
fun deleteAllBolusCalculatorResults() = fun deleteAllBolusCalculatorResults() =
database.bolusCalculatorResultDao.deleteAllEntries() database.bolusCalculatorResultDao.deleteAllEntries()
// DEVICE STATUS
fun insert(deviceStatus: DeviceStatus) : Long =
database.deviceStatusDao.insert(deviceStatus)
/*
* returns a Pair of the next entity to sync and the ID of the "update".
* The update id might either be the entry id itself if it is a new entry - or the id
* of the update ("historic") entry. The sync counter should be incremented to that id if it was synced successfully.
*
* It is a Maybe as there might be no next element.
* */
fun getNextSyncElementDeviceStatus(id: Long): Maybe<DeviceStatus> =
database.deviceStatusDao.getNextModifiedOrNewAfter(id)
.subscribeOn(Schedulers.io())
fun getModifiedDeviceStatusDataFromId(lastId: Long): Single<List<DeviceStatus>> =
database.deviceStatusDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
} }
@Suppress("USELESS_CAST") @Suppress("USELESS_CAST")

View file

@ -24,5 +24,6 @@ internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val datab
val userEntryDao: UserEntryDao = DelegatedUserEntryDao(changes, database.userEntryDao) val userEntryDao: UserEntryDao = DelegatedUserEntryDao(changes, database.userEntryDao)
val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao) val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao)
val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao) val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao)
val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao)
fun clearAllTables() = database.clearAllTables() fun clearAllTables() = database.clearAllTables()
} }

View file

@ -5,11 +5,11 @@ const val TABLE_APS_RESULT_LINKS = "apsResultLinks"
const val TABLE_BOLUSES = "boluses" const val TABLE_BOLUSES = "boluses"
const val TABLE_BOLUS_CALCULATOR_RESULTS = "bolusCalculatorResults" const val TABLE_BOLUS_CALCULATOR_RESULTS = "bolusCalculatorResults"
const val TABLE_CARBS = "carbs" const val TABLE_CARBS = "carbs"
const val TABLE_DEVICE_STATUS = "deviceStatus"
const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches" const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches"
const val TABLE_EXTENDED_BOLUSES = "extendedBoluses" const val TABLE_EXTENDED_BOLUSES = "extendedBoluses"
const val TABLE_GLUCOSE_VALUES = "glucoseValues" const val TABLE_GLUCOSE_VALUES = "glucoseValues"
const val TABLE_FOODS = "foods" const val TABLE_FOODS = "foods"
const val TABLE_MEAL_LINKS = "mealLinks"
const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks" const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks"
const val TABLE_PROFILE_SWITCHES = "profileSwitches" const val TABLE_PROFILE_SWITCHES = "profileSwitches"
const val TABLE_TEMPORARY_BASALS = "temporaryBasals" const val TABLE_TEMPORARY_BASALS = "temporaryBasals"

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.TABLE_DEVICE_STATUS
import io.reactivex.Maybe
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface DeviceStatusDao {
@Insert
fun insert(entry: DeviceStatus): Long
@Update
fun update(entry: DeviceStatus)
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE id = :id")
fun findById(id: Long): DeviceStatus?
@Query("DELETE FROM $TABLE_DEVICE_STATUS")
fun deleteAllEntries()
@Query("DELETE FROM $TABLE_DEVICE_STATUS WHERE id NOT IN (SELECT MAX(id) FROM $TABLE_DEVICE_STATUS)")
fun deleteAllEntriesExceptLast()
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE nightscoutId = :nsId")
fun findByNSId(nsId: String): DeviceStatus?
// This query will be used with v3 to get all changed records
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE id > :id ORDER BY id ASC")
fun getModifiedFrom(id: Long): Single<List<DeviceStatus>>
// for WS we need 1 record only
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE id > :id ORDER BY id ASC limit 1")
fun getNextModifiedOrNewAfter(id: Long): Maybe<DeviceStatus>
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.DeviceStatusDao
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedDeviceStatusDao(changes: MutableList<DBEntry>, private val dao: DeviceStatusDao) : DelegatedDao(changes), DeviceStatusDao by dao

View file

@ -0,0 +1,44 @@
package info.nightscout.androidaps.database.entities
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import info.nightscout.androidaps.database.TABLE_DEVICE_STATUS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import java.util.*
@Entity(tableName = TABLE_DEVICE_STATUS,
foreignKeys = [],
indices = [Index("timestamp")])
data class DeviceStatus(
@PrimaryKey(autoGenerate = true)
var id: Long = 0,
@Embedded
var interfaceIDs_backing: InterfaceIDs? = null,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var device: String? = null,
var pump: String? = null,
var enacted: String? = null,
var suggested: String? = null,
var iob: String? = null,
var uploaderBattery: Int = 0,
var configuration: String? = null
) : DBEntryWithTime {
var interfaceIDs: InterfaceIDs
get() {
var value = this.interfaceIDs_backing
if (value == null) {
value = InterfaceIDs()
interfaceIDs_backing = value
}
return value
}
set(value) {
interfaceIDs_backing = value
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.DeviceStatus
class UpdateNsIdDeviceStatusTransaction(val deviceStatus: DeviceStatus) : Transaction<Unit>() {
override fun run() {
val current = database.deviceStatusDao.findById(deviceStatus.id)
if (current != null && current.interfaceIDs.nightscoutId != deviceStatus.interfaceIDs.nightscoutId)
database.deviceStatusDao.update(deviceStatus)
}
}