Automatic timeZone and DST handling

This commit is contained in:
jbr7rr 2023-06-25 21:52:01 +02:00
parent 201fb5768a
commit 19b9ca1da4
15 changed files with 189 additions and 14 deletions

View file

@ -136,6 +136,7 @@ open class Notification {
const val BLUETOOTH_NOT_ENABLED = 82
const val PATCH_NOT_ACTIVE = 83
const val PUMP_SETTINGS_FAILED = 84
const val PUMP_TIMEZONE_UPDATE_FAILED = 85
const val USER_MESSAGE = 1000

View file

@ -9,4 +9,5 @@ interface Medtrum {
fun setUserOptions(): PumpEnactResult // set user settings
fun clearAlarms(): PumpEnactResult // clear alarms
fun deactivate(): PumpEnactResult // deactivate patch
fun updateTime(): PumpEnactResult // update time
}

View file

@ -30,6 +30,7 @@ abstract class Command(
STOP_PUMP,
CLEAR_ALARMS, // so far only Medtrum specific
DEACTIVATE, // so far only Medtrum specific
UPDATE_TIME, // so far only Medtrum specific
INSIGHT_SET_TBR_OVER_ALARM, // insight only
CUSTOM_COMMAND
}

View file

@ -33,6 +33,7 @@ interface CommandQueue {
fun loadEvents(callback: Callback?): Boolean
fun clearAlarms(callback: Callback?): Boolean
fun deactivate(callback: Callback?): Boolean
fun updateTime(callback: Callback?): Boolean
fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean
fun isCustomCommandRunning(customCommandType: Class<out CustomCommand>): Boolean
fun isCustomCommandInQueue(customCommandType: Class<out CustomCommand>): Boolean

View file

@ -395,6 +395,7 @@
<string name="load_events">LOAD EVENTS</string>
<string name="clear_alarms">CLEAR_ALARMS</string>
<string name="deactivate">DEACTIVATE</string>
<string name="update_time">UPDATE TIME</string>
<string name="load_history">LOAD HISTORY %1$d</string>
<string name="load_tdds">LOAD TDDs</string>
<string name="set_profile">SET PROFILE</string>

View file

@ -22,6 +22,7 @@ import info.nightscout.implementation.queue.commands.CommandInsightSetTBROverNot
import info.nightscout.implementation.queue.commands.CommandLoadEvents
import info.nightscout.implementation.queue.commands.CommandLoadHistory
import info.nightscout.implementation.queue.commands.CommandLoadTDDs
import info.nightscout.implementation.queue.commands.CommandUpdateTime
@Module
@Suppress("unused")
@ -36,6 +37,7 @@ abstract class CommandQueueModule {
@ContributesAndroidInjector abstract fun commandLoadEventsInjector(): CommandLoadEvents
@ContributesAndroidInjector abstract fun commandClearAlarmsInjector(): CommandClearAlarms
@ContributesAndroidInjector abstract fun commandDeactivateInjector(): CommandDeactivate
@ContributesAndroidInjector abstract fun commandUpdateTimeInjector(): CommandUpdateTime
@ContributesAndroidInjector abstract fun commandLoadHistoryInjector(): CommandLoadHistory
@ContributesAndroidInjector abstract fun commandLoadTDDsInjector(): CommandLoadTDDs
@ContributesAndroidInjector abstract fun commandReadStatusInjector(): CommandReadStatus

View file

@ -38,6 +38,7 @@ import info.nightscout.implementation.queue.commands.CommandStartPump
import info.nightscout.implementation.queue.commands.CommandStopPump
import info.nightscout.implementation.queue.commands.CommandTempBasalAbsolute
import info.nightscout.implementation.queue.commands.CommandTempBasalPercent
import info.nightscout.implementation.queue.commands.CommandUpdateTime
import info.nightscout.interfaces.AndroidPermission
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.constraints.Constraint
@ -564,6 +565,19 @@ class CommandQueueImplementation @Inject constructor(
return true
}
override fun updateTime(callback: Callback?): Boolean {
if (isRunning(CommandType.UPDATE_TIME)) {
callback?.result(executingNowError())?.run()
return false
}
// remove all unfinished
removeAll(CommandType.UPDATE_TIME)
// add new command to queue
add(CommandUpdateTime(injector, callback))
notifyAboutNewCommand()
return true
}
override fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean {
if (isCustomCommandInQueue(customCommand.javaClass)) {
callback?.result(executingNowError())?.run()

View file

@ -0,0 +1,39 @@
package info.nightscout.implementation.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.pump.Dana
import info.nightscout.interfaces.pump.Diaconn
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.Command
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class CommandUpdateTime(
injector: HasAndroidInjector,
callback: Callback?
) : Command(injector, CommandType.UPDATE_TIME, callback) {
@Inject lateinit var activePlugin: ActivePlugin
override fun execute() {
val pump = activePlugin.activePump
if (pump is Medtrum) {
val medtrumPump = pump as Medtrum
val r = medtrumPump.updateTime()
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run()
}
}
override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.update_time)
override fun log(): String = "UPDATE TIME"
override fun cancel() {
aapsLogger.debug(LTag.PUMPQUEUE, "Result cancel")
callback?.result(PumpEnactResult(injector).success(false).comment(info.nightscout.core.ui.R.string.connectiontimedout))?.run()
}
}

View file

@ -246,6 +246,10 @@ class CommandQueueImplementationTest : TestBaseWithProfile() {
// add deactivate
commandQueue.deactivate(null)
Assertions.assertEquals(6, commandQueue.size())
// add updateTime
commandQueue.updateTime(null)
Assertions.assertEquals(7, commandQueue.size())
commandQueue.clear()
commandQueue.tempBasalAbsolute(0.0, 30, true, validProfile, PumpSync.TemporaryBasalType.NORMAL, null)
@ -395,6 +399,22 @@ class CommandQueueImplementationTest : TestBaseWithProfile() {
Assertions.assertEquals(1, commandQueue.size())
}
@Test
fun isUpdateTimeCommandInQueue() {
// given
Assertions.assertEquals(0, commandQueue.size())
// when
commandQueue.updateTime(null)
// then
Assertions.assertTrue(commandQueue.isLastScheduled(Command.CommandType.UPDATE_TIME))
Assertions.assertEquals(1, commandQueue.size())
// next should be ignored
commandQueue.updateTime(null)
Assertions.assertEquals(1, commandQueue.size())
}
@Test
fun isLoadTDDsCommandInQueue() {
// given

View file

@ -29,6 +29,7 @@ import info.nightscout.interfaces.pump.actions.CustomActionType
import info.nightscout.interfaces.pump.defs.ManufacturerType
import info.nightscout.interfaces.pump.defs.PumpDescription
import info.nightscout.interfaces.pump.defs.PumpType
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.queue.CustomCommand
import info.nightscout.interfaces.ui.UiInteraction
@ -394,10 +395,6 @@ import kotlin.math.round
return PumpEnactResult(injector) // Note: Can implement this if we implement history fully (no priority)
}
override fun canHandleDST(): Boolean {
return false
}
override fun getCustomActions(): List<CustomAction>? {
return null
}
@ -409,7 +406,20 @@ import kotlin.math.round
return null
}
override fun canHandleDST(): Boolean {
return true
}
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {
medtrumPump.needTimeUpdate = true
// Update status to sync time with pump
if (isInitialized()) {
commandQueue.updateTime(object : Callback() {
override fun run() {
medtrumService?.timeUpdateNotification(this.result.success)
}
})
}
}
// Medtrum interface
@ -452,4 +462,14 @@ import kotlin.math.round
val connectionOK = medtrumService?.deactivatePatch() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
override fun updateTime(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
val connectionOK = medtrumService?.updateTime() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
}

View file

@ -177,7 +177,7 @@ class MedtrumPump @Inject constructor(
_deviceType = value
sp.putInt(R.string.key_device_type, value)
}
private var _swVersion: String = "" // As reported by pump
var swVersion: String
get() = _swVersion
@ -185,7 +185,7 @@ class MedtrumPump @Inject constructor(
_swVersion = value
sp.putString(R.string.key_sw_version, value)
}
private var _patchStartTime = 0L // Time in ms!
var patchStartTime: Long
get() = _patchStartTime
@ -194,10 +194,19 @@ class MedtrumPump @Inject constructor(
sp.putLong(R.string.key_patch_start_time, value)
}
private var _pumpTimeZoneOffset = 0 // As reported by pump
var pumpTimeZoneOffset: Int
get() = _pumpTimeZoneOffset
set(value) {
_pumpTimeZoneOffset = value
sp.putInt(R.string.key_pump_time_zone_offset, value)
}
private var _pumpSN = 0L
val pumpSN: Long
get() = _pumpSN
var needTimeUpdate = false
var lastTimeReceivedFromPump = 0L // Time in ms! // TODO: Consider removing as is not used?
var suspendTime = 0L // Time in ms!
var patchAge = 0L // Time in seconds?! // TODO: Not used
@ -218,7 +227,7 @@ class MedtrumPump @Inject constructor(
private var _lastBasalPatchId = 0L
val lastBasalPatchId: Long
get() = _lastBasalPatchId
private var _lastBasalStartTime = 0L
val lastBasalStartTime: Long
get() = _lastBasalStartTime
@ -255,6 +264,7 @@ class MedtrumPump @Inject constructor(
_deviceType = sp.getInt(R.string.key_device_type, 0)
_swVersion = sp.getString(R.string.key_sw_version, "")
_patchStartTime = sp.getLong(R.string.key_patch_start_time, 0L)
_pumpTimeZoneOffset = sp.getInt(R.string.key_pump_time_zone_offset, 0)
loadActiveAlarms()
@ -266,10 +276,10 @@ class MedtrumPump @Inject constructor(
}
}
fun pumpType(): PumpType =
when(deviceType) {
fun pumpType(): PumpType =
when (deviceType) {
80, 88 -> PumpType.MEDTRUM_NANO
else -> PumpType.MEDTRUM_UNTESTED
else -> PumpType.MEDTRUM_UNTESTED
}
fun loadUserSettingsFromSP() {
@ -464,7 +474,7 @@ class MedtrumPump @Inject constructor(
activeAlarms.add(alarm)
saveActiveAlarms()
}
fun removeAlarm(alarm: AlarmState) {
activeAlarms.remove(alarm)
saveActiveAlarms()
@ -504,7 +514,7 @@ class MedtrumPump @Inject constructor(
val alarmsStr = activeAlarms.joinToString(separator = ",") { it.name }
sp.putString(R.string.key_active_alarms, alarmsStr)
}
private fun loadActiveAlarms() {
val alarmsStr = sp.getString(R.string.key_active_alarms, "")
if (alarmsStr.isNullOrEmpty()) {

View file

@ -1,6 +1,7 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_TIME_ZONE
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
@ -10,6 +11,9 @@ import javax.inject.Inject
class SetTimeZonePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var medtrumPump: MedtrumPump
var offsetMins: Int = 0
init {
opCode = SET_TIME_ZONE.code
@ -17,8 +21,16 @@ class SetTimeZonePacket(injector: HasAndroidInjector) : MedtrumPacket(injector)
override fun getRequest(): ByteArray {
val time = MedtrumTimeUtil().getCurrentTimePumpSeconds()
var offsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now())
offsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now())
if (offsetMins < 0) offsetMins += 65536
return byteArrayOf(opCode) + offsetMins.toByteArray(2) + time.toByteArray(4)
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
medtrumPump.pumpTimeZoneOffset = offsetMins
}
return success
}
}

View file

@ -189,10 +189,44 @@ class MedtrumService : DaggerService(), BLECommCallback {
}
fun readPumpStatus() {
// Update pump events
rxBus.send(EventPumpStatusChanged(rh.gs(info.nightscout.pump.medtrum.R.string.gettingpumpstatus)))
// Update time if needed
if (medtrumPump.needTimeUpdate || medtrumPump.pumpTimeZoneOffset != dateUtil.getTimeZoneOffsetMinutes(dateUtil.now())) {
aapsLogger.warn(LTag.PUMPCOMM, "Pump time update from readPumpStatus")
timeUpdateNotification(updateTime(false))
}
// Update history
loadEvents()
}
fun timeUpdateNotification(updateSuccess: Boolean) {
if (updateSuccess) {
aapsLogger.debug(LTag.PUMPCOMM, "Pump time updated")
medtrumPump.needTimeUpdate = false
uiInteraction.addNotification(
Notification.INSIGHT_DATE_TIME_UPDATED, // :---)
rh.gs(info.nightscout.core.ui.R.string.pump_time_updated),
Notification.INFO,
)
} else {
aapsLogger.error(LTag.PUMPCOMM, "Failed to update pump time")
uiInteraction.addNotification(
Notification.PUMP_TIMEZONE_UPDATE_FAILED,
rh.gs(R.string.pump_time_update_failed),
Notification.URGENT,
)
}
}
fun updateTime(needLoadHistory: Boolean = true): Boolean {
var result = sendPacketAndGetResponse(SetTimePacket(injector))
if (result) result = sendPacketAndGetResponse(SetTimeZonePacket(injector))
if (needLoadHistory) {
if (result) result = loadEvents()
}
return result
}
fun loadEvents(): Boolean {
rxBus.send(EventPumpStatusChanged(rh.gs(info.nightscout.pump.medtrum.R.string.gettingpumpstatus)))
// Sync records (based on the info we have from the sync)
@ -785,6 +819,8 @@ class MedtrumService : DaggerService(), BLECommCallback {
// Succes!
responseHandled = true
responseSuccess = true
medtrumPump.needTimeUpdate = false
timeUpdateNotification(true)
toState(SynchronizeState())
} else if (mPacket?.failed == true) {
// Failure

View file

@ -21,6 +21,7 @@
<string name="key_actual_basal_profile" translatable="false">actual_basal_profile</string>
<string name="key_current_sequence_number" translatable="false">current_sequence_number</string>
<string name="key_synced_sequence_number" translatable="false">synced_sequence_number</string>
<string name="key_pump_time_zone_offset" translatable="false">pump_time_zone_offset</string>
<string name="medtrum">Medtrum</string>
<string name="medtrum_pump_shortname">MT</string>
@ -74,6 +75,7 @@
<string name="alarm_base_fault">Base fault</string>
<string name="alarm_battery_out">Battery out</string>
<string name="alarm_no_calibration">No calibration</string>
<string name="pump_time_update_failed">Failed to update pump timezone, snooze message and refresh manually.</string>
<!-- wizard-->
<string name="string_start_complete">Complete</string>

View file

@ -38,4 +38,19 @@ class SetTimeZonePacketTest : MedtrumTestBase() {
assertEquals(7, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
@Test fun handleResponseGivenPacketWhenValuesSetThenReturnCorrectValues() {
// Inputs
val response = byteArrayOf(7, 10, 3, 0, 0, 0, -38)
// Call
val packet = SetTimeZonePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
val expectedOffsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now())
assertTrue(result)
assertEquals(expectedOffsetMins, medtrumPump.pumpTimeZoneOffset)
}
}