Minor improvements on previous commit

This commit is contained in:
Bart Sopers 2020-09-14 19:52:56 +02:00
parent feecfc5676
commit 06c5999807
9 changed files with 112 additions and 108 deletions

View file

@ -209,8 +209,12 @@ class CommandQueueTest : TestBaseWithProfile() {
Assert.assertTrue(queued2) Assert.assertTrue(queued2)
Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand1::class.java)) Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand1::class.java))
Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand2::class.java)) Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand2::class.java))
Assert.assertFalse(commandQueue.isCustomCommandInQueue(CustomCommand3::class.java))
Assert.assertTrue(commandQueue.isCustomCommandRunning(CustomCommand1::class.java)) Assert.assertTrue(commandQueue.isCustomCommandRunning(CustomCommand1::class.java))
Assert.assertFalse(commandQueue.isCustomCommandRunning(CustomCommand2::class.java)) Assert.assertFalse(commandQueue.isCustomCommandRunning(CustomCommand2::class.java))
Assert.assertFalse(commandQueue.isCustomCommandRunning(CustomCommand3::class.java))
Assert.assertEquals(1, commandQueue.size()) Assert.assertEquals(1, commandQueue.size())
} }

View file

@ -119,7 +119,7 @@ public interface PumpInterface {
List<CustomAction> getCustomActions(); List<CustomAction> getCustomActions();
/** /**
* Executes a custom action. Please not that these actions will not be queued * Executes a custom action. Please note that these actions will not be queued
* *
* @param customActionType action to be executed * @param customActionType action to be executed
*/ */
@ -127,7 +127,7 @@ public interface PumpInterface {
/** /**
* Executes a custom queued command * Executes a custom queued command
* To place a custom command on the queue, see {@link CommandQueueProvider#customCommand(CustomCommand, Callback)} * See {@link CommandQueueProvider#customCommand(CustomCommand, Callback)} for queuing a custom command.
* *
* @param customCommand the custom command to be executed * @param customCommand the custom command to be executed
* @return PumpEnactResult that represents the command execution result * @return PumpEnactResult that represents the command execution result

View file

@ -4,7 +4,7 @@ import java.io.Serializable
/** /**
* Implement this interface for every custom pump command that you want to be able to queue * Implement this interface for every custom pump command that you want to be able to queue
* See [info.nightscout.androidaps.interfaces.CommandQueueProvider] for queuing a custom command. * See [info.nightscout.androidaps.interfaces.CommandQueueProvider.customCommand] for queuing a custom command.
*/ */
interface CustomCommand : Serializable { interface CustomCommand : Serializable {

View file

@ -106,7 +106,7 @@ import io.reactivex.schedulers.Schedulers;
public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, RileyLinkPumpDevice { public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, RileyLinkPumpDevice {
private static final long RILEY_LINK_CONNECT_TIMEOUT_MILLIS = 3 * 60 * 1000L; // 3 minutes private static final long RILEY_LINK_CONNECT_TIMEOUT_MILLIS = 3 * 60 * 1000L; // 3 minutes
private static final long STATUS_CHECK_INTERVAL_MILLIS = 60 * 1000L; // 1 minute private static final long STATUS_CHECK_INTERVAL_MILLIS = 60 * 1000L; // 1 minute
public static final int STARTUP_STATUS_REQUEST_TRIES = 3; public static final int STARTUP_STATUS_REQUEST_TRIES = 2;
private final PodStateManager podStateManager; private final PodStateManager podStateManager;
private final RileyLinkServiceData rileyLinkServiceData; private final RileyLinkServiceData rileyLinkServiceData;
@ -447,7 +447,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
} }
/** /**
* The only actual status requests we send here to the Pod are on startup (in initializeAfterRileyLinkConnection) * The only actual status requests we send to the Pod here are on startup (in {@link #initializeAfterRileyLinkConnection() initializeAfterRileyLinkConnection()})
* And when the user explicitly requested it by clicking the Refresh button on the Omnipod tab (which is executed through {@link #executeCustomCommand(CustomCommand)}) * And when the user explicitly requested it by clicking the Refresh button on the Omnipod tab (which is executed through {@link #executeCustomCommand(CustomCommand)})
* We don't do periodical status requests because that could drain the Pod's battery * We don't do periodical status requests because that could drain the Pod's battery
*/ */
@ -921,8 +921,8 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
private void initializeAfterRileyLinkConnection() { private void initializeAfterRileyLinkConnection() {
if (podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus().isAtLeast(PodProgressStatus.PAIRING_COMPLETED)) { if (podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus().isAtLeast(PodProgressStatus.PAIRING_COMPLETED)) {
PumpEnactResult result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
for (int i = 0; STARTUP_STATUS_REQUEST_TRIES > i; i++) { for (int i = 0; STARTUP_STATUS_REQUEST_TRIES > i; i++) {
PumpEnactResult result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
if (result.success) { if (result.success) {
aapsLogger.debug(LTag.PUMP, "Successfully retrieved Pod status on startup"); aapsLogger.debug(LTag.PUMP, "Successfully retrieved Pod status on startup");
break; break;

View file

@ -19,7 +19,7 @@ public enum OmnipodCommandType {
SET_TIME(R.string.omnipod_cmd_set_time), // SET_TIME(R.string.omnipod_cmd_set_time), //
CONFIGURE_ALERTS(R.string.omnipod_cmd_configure_alerts), // CONFIGURE_ALERTS(R.string.omnipod_cmd_configure_alerts), //
ACKNOWLEDGE_ALERTS(R.string.omnipod_cmd_acknowledge_alerts), // ACKNOWLEDGE_ALERTS(R.string.omnipod_cmd_acknowledge_alerts), //
READ_POD_PULSE_LOG(R.string.omnipod_cmd_get_pulse_log), // READ_POD_PULSE_LOG(R.string.omnipod_cmd_read_pulse_log), //
SUSPEND_DELIVERY(R.string.omnipod_cmd_suspend_delivery), SUSPEND_DELIVERY(R.string.omnipod_cmd_suspend_delivery),
RESUME_DELIVERY(R.string.omnipod_cmd_resume_delivery); RESUME_DELIVERY(R.string.omnipod_cmd_resume_delivery);

View file

@ -59,7 +59,7 @@ import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.SingleSubject; import io.reactivex.subjects.SingleSubject;
public class OmnipodManager { public class OmnipodManager {
private static final int ACTION_VERIFICATION_TRIES = 3; private static final int ACTION_VERIFICATION_TRIES = 2;
private final OmnipodRileyLinkCommunicationManager communicationService; private final OmnipodRileyLinkCommunicationManager communicationService;
private PodStateManager podStateManager; private PodStateManager podStateManager;
@ -548,9 +548,9 @@ public class OmnipodManager {
try { try {
PodInfoResponse podInfoResponse = communicationService.executeAction(new GetPodInfoAction(podStateManager, PodInfoType.RECENT_PULSE_LOG)); PodInfoResponse podInfoResponse = communicationService.executeAction(new GetPodInfoAction(podStateManager, PodInfoType.RECENT_PULSE_LOG));
PodInfoRecentPulseLog pulseLogInfo = (PodInfoRecentPulseLog) podInfoResponse.getPodInfo(); PodInfoRecentPulseLog pulseLogInfo = (PodInfoRecentPulseLog) podInfoResponse.getPodInfo();
aapsLogger.info(LTag.PUMPCOMM, "Retrieved pulse log from the pod: {}", pulseLogInfo.toString()); aapsLogger.info(LTag.PUMPCOMM, "Read pulse log from the pod: {}", pulseLogInfo.toString());
} catch (Exception ex) { } catch (Exception ex) {
aapsLogger.warn(LTag.PUMPCOMM, "Failed to retrieve pulse log from the pod", ex); aapsLogger.warn(LTag.PUMPCOMM, "Failed to read pulse log", ex);
} }
try { try {

View file

@ -108,13 +108,13 @@ class OmnipodFragment : DaggerFragment() {
omnipod_button_resume_delivery.setOnClickListener { omnipod_button_resume_delivery.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
commandQueue.customCommand(CommandResumeDelivery(), commandQueue.customCommand(CommandResumeDelivery(),
ErrorDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_resume_delivery), true).messageOnSuccess(resourceHelper.gs(R.string.omnipod_delivery_resumed))) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_resume_delivery), true).messageOnSuccess(resourceHelper.gs(R.string.omnipod_delivery_resumed)))
} }
omnipod_button_refresh_status.setOnClickListener { omnipod_button_refresh_status.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
commandQueue.customCommand(CommandGetPodStatus(), commandQueue.customCommand(CommandGetPodStatus(),
ErrorDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_refresh_status), false)) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_refresh_status), false))
} }
omnipod_button_rileylink_stats.setOnClickListener { omnipod_button_rileylink_stats.setOnClickListener {
@ -128,28 +128,28 @@ class OmnipodFragment : DaggerFragment() {
omnipod_button_acknowledge_active_alerts.setOnClickListener { omnipod_button_acknowledge_active_alerts.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
commandQueue.customCommand(CommandAcknowledgeAlerts(), commandQueue.customCommand(CommandAcknowledgeAlerts(),
ErrorDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_acknowledge_alerts), false) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_acknowledge_alerts), false)
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_acknowledged_alerts))) .messageOnSuccess(resourceHelper.gs(R.string.omnipod_acknowledged_alerts)))
} }
omnipod_button_suspend_delivery.setOnClickListener { omnipod_button_suspend_delivery.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
commandQueue.customCommand(CommandSuspendDelivery(), commandQueue.customCommand(CommandSuspendDelivery(),
ErrorDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_suspend_delivery), true) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_suspend_delivery), true)
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_suspended_delivery))) .messageOnSuccess(resourceHelper.gs(R.string.omnipod_suspended_delivery)))
} }
omnipod_button_set_time.setOnClickListener { omnipod_button_set_time.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
commandQueue.customCommand(CommandHandleTimeChange(true), commandQueue.customCommand(CommandHandleTimeChange(true),
ErrorDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_set_time), true) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_set_time), true)
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_time_on_pod_updated))) .messageOnSuccess(resourceHelper.gs(R.string.omnipod_time_on_pod_updated)))
} }
omnipod_button_pulse_log.setOnClickListener { omnipod_button_pulse_log.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
commandQueue.customCommand(CommandReadPulseLog(), commandQueue.customCommand(CommandReadPulseLog(),
ErrorDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_retrieve_pulse_log), false)) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_failed_to_read_pulse_log), false))
} }
} }
@ -511,105 +511,105 @@ class OmnipodFragment : DaggerFragment() {
OKDialog.show(it, resourceHelper.gs(R.string.omnipod_warning), OKDialog.show(it, resourceHelper.gs(R.string.omnipod_warning),
resourceHelper.gs(R.string.omnipod_error_operation_not_possible_no_configuration), null) resourceHelper.gs(R.string.omnipod_error_operation_not_possible_no_configuration), null)
}).run() }).run()
}
}
private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
context?.let {
val i = Intent(it, ErrorHelperActivity::class.java)
i.putExtra("soundid", if (withSound) R.raw.boluserror else 0)
i.putExtra("status", message)
i.putExtra("title", title)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
it.startActivity(i)
}
}
private fun displayOkDialog(title: String, message: String) {
context?.let {
UIRunnable(Runnable {
OKDialog.show(it, title, message, null)
}).run()
}
}
private fun readableZonedTime(time: DateTime): String {
val timeAsJavaData = time.toLocalDateTime().toDate()
val timeZone = podStateManager.timeZone.toTimeZone()
if (timeZone == TimeZone.getDefault()) {
return dateUtil.dateAndTimeString(timeAsJavaData)
}
val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData)
val locale = resources.configuration.locales.get(0)
val timeZoneDisplayName = timeZone.getDisplayName(isDaylightTime, TimeZone.SHORT, locale) + " " + timeZone.getDisplayName(isDaylightTime, TimeZone.LONG, locale)
return resourceHelper.gs(R.string.omnipod_pod_time_with_timezone, dateUtil.dateAndTimeString(timeAsJavaData), timeZoneDisplayName)
}
private fun readableDuration(dateTime: DateTime): String {
val duration = Duration(dateTime, DateTime.now())
val hours = duration.standardHours.toInt()
val minutes = duration.standardMinutes.toInt()
val seconds = duration.standardSeconds.toInt()
when {
seconds < 10 -> {
return resourceHelper.gs(R.string.omnipod_moments_ago)
}
seconds < 60 -> {
return resourceHelper.gs(R.string.omnipod_less_than_a_minute_ago)
}
seconds < 60 * 60 -> { // < 1 hour
return resourceHelper.gs(R.string.omnipod_time_ago, resourceHelper.gq(R.plurals.omnipod_minutes, minutes, minutes))
}
seconds < 24 * 60 * 60 -> { // < 1 day
val minutesLeft = minutes % 60
if (minutesLeft > 0)
return resourceHelper.gs(R.string.omnipod_time_ago,
resourceHelper.gs(R.string.omnipod_composite_time, resourceHelper.gq(R.plurals.omnipod_hours, hours, hours), resourceHelper.gq(R.plurals.omnipod_minutes, minutesLeft, minutesLeft)))
return resourceHelper.gs(R.string.omnipod_time_ago, resourceHelper.gq(R.plurals.omnipod_hours, hours, hours))
}
else -> {
val days = hours / 24
val hoursLeft = hours % 24
if (hoursLeft > 0)
return resourceHelper.gs(R.string.omnipod_time_ago,
resourceHelper.gs(R.string.omnipod_composite_time, resourceHelper.gq(R.plurals.omnipod_days, days, days), resourceHelper.gq(R.plurals.omnipod_hours, hoursLeft, hoursLeft)))
return resourceHelper.gs(R.string.omnipod_time_ago, resourceHelper.gq(R.plurals.omnipod_days, days, days))
} }
} }
}
private fun isQueueEmpty(): Boolean { private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
return commandQueue.size() == 0 && commandQueue.performing() == null context?.let {
} val i = Intent(it, ErrorHelperActivity::class.java)
i.putExtra("soundid", if (withSound) R.raw.boluserror else 0)
i.putExtra("status", message)
i.putExtra("title", title)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
it.startActivity(i)
}
}
// FIXME ideally we should just have access to LocalAlertUtils here private fun displayOkDialog(title: String, message: String) {
private fun getPumpUnreachableTimeout(): Duration { context?.let {
return Duration.standardMinutes(sp.getInt(resourceHelper.gs(R.string.key_pump_unreachable_threshold_minutes), Constants.DEFAULT_PUMP_UNREACHABLE_THRESHOLD_MINUTES).toLong()) UIRunnable(Runnable {
} OKDialog.show(it, title, message, null)
}).run()
}
}
inner class ErrorDialogCallback(private val errorMessagePrefix: String, private val withSoundOnError: Boolean) : Callback() { private fun readableZonedTime(time: DateTime): String {
private var messageOnSuccess: String? = null val timeAsJavaData = time.toLocalDateTime().toDate()
val timeZone = podStateManager.timeZone.toTimeZone()
if (timeZone == TimeZone.getDefault()) {
return dateUtil.dateAndTimeString(timeAsJavaData)
}
override fun run() { val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData)
if (result.success) { val locale = resources.configuration.locales.get(0)
val messageOnSuccess = this.messageOnSuccess val timeZoneDisplayName = timeZone.getDisplayName(isDaylightTime, TimeZone.SHORT, locale) + " " + timeZone.getDisplayName(isDaylightTime, TimeZone.LONG, locale)
if (messageOnSuccess != null) { return resourceHelper.gs(R.string.omnipod_pod_time_with_timezone, dateUtil.dateAndTimeString(timeAsJavaData), timeZoneDisplayName)
displayOkDialog(resourceHelper.gs(R.string.omnipod_confirmation), messageOnSuccess) }
private fun readableDuration(dateTime: DateTime): String {
val duration = Duration(dateTime, DateTime.now())
val hours = duration.standardHours.toInt()
val minutes = duration.standardMinutes.toInt()
val seconds = duration.standardSeconds.toInt()
when {
seconds < 10 -> {
return resourceHelper.gs(R.string.omnipod_moments_ago)
}
seconds < 60 -> {
return resourceHelper.gs(R.string.omnipod_less_than_a_minute_ago)
}
seconds < 60 * 60 -> { // < 1 hour
return resourceHelper.gs(R.string.omnipod_time_ago, resourceHelper.gq(R.plurals.omnipod_minutes, minutes, minutes))
}
seconds < 24 * 60 * 60 -> { // < 1 day
val minutesLeft = minutes % 60
if (minutesLeft > 0)
return resourceHelper.gs(R.string.omnipod_time_ago,
resourceHelper.gs(R.string.omnipod_composite_time, resourceHelper.gq(R.plurals.omnipod_hours, hours, hours), resourceHelper.gq(R.plurals.omnipod_minutes, minutesLeft, minutesLeft)))
return resourceHelper.gs(R.string.omnipod_time_ago, resourceHelper.gq(R.plurals.omnipod_hours, hours, hours))
}
else -> {
val days = hours / 24
val hoursLeft = hours % 24
if (hoursLeft > 0)
return resourceHelper.gs(R.string.omnipod_time_ago,
resourceHelper.gs(R.string.omnipod_composite_time, resourceHelper.gq(R.plurals.omnipod_days, days, days), resourceHelper.gq(R.plurals.omnipod_hours, hoursLeft, hoursLeft)))
return resourceHelper.gs(R.string.omnipod_time_ago, resourceHelper.gq(R.plurals.omnipod_days, days, days))
} }
} else {
displayErrorDialog(resourceHelper.gs(R.string.omnipod_warning), resourceHelper.gs(R.string.omnipod_two_strings_concatenated_by_colon, errorMessagePrefix, result.comment), withSoundOnError)
} }
} }
fun messageOnSuccess(message: String): ErrorDialogCallback { private fun isQueueEmpty(): Boolean {
messageOnSuccess = message return commandQueue.size() == 0 && commandQueue.performing() == null
return this }
// FIXME ideally we should just have access to LocalAlertUtils here
private fun getPumpUnreachableTimeout(): Duration {
return Duration.standardMinutes(sp.getInt(resourceHelper.gs(R.string.key_pump_unreachable_threshold_minutes), Constants.DEFAULT_PUMP_UNREACHABLE_THRESHOLD_MINUTES).toLong())
}
inner class DisplayResultDialogCallback(private val errorMessagePrefix: String, private val withSoundOnError: Boolean) : Callback() {
private var messageOnSuccess: String? = null
override fun run() {
if (result.success) {
val messageOnSuccess = this.messageOnSuccess
if (messageOnSuccess != null) {
displayOkDialog(resourceHelper.gs(R.string.omnipod_confirmation), messageOnSuccess)
}
} else {
displayErrorDialog(resourceHelper.gs(R.string.omnipod_warning), resourceHelper.gs(R.string.omnipod_two_strings_concatenated_by_colon, errorMessagePrefix, result.comment), withSoundOnError)
}
}
fun messageOnSuccess(message: String): DisplayResultDialogCallback {
messageOnSuccess = message
return this
}
} }
}
} }

View file

@ -145,7 +145,7 @@
<string name="omnipod_suspend_delivery_short">Suspendare</string> <string name="omnipod_suspend_delivery_short">Suspendare</string>
<string name="omnipod_cmd_pair_and_prime">Împerechere și amorsare</string> <string name="omnipod_cmd_pair_and_prime">Împerechere și amorsare</string>
<string name="omnipod_cmd_fill_cannula_set_basal_profile">Umpleţi canula şi setaţi profilul bazal</string> <string name="omnipod_cmd_fill_cannula_set_basal_profile">Umpleţi canula şi setaţi profilul bazal</string>
<string name="omnipod_cmd_get_pulse_log">Obțineți jurnalul pulsurilor</string> <string name="omnipod_cmd_read_pulse_log">Obțineți jurnalul pulsurilor</string>
<string name="omnipod_uncertain_failure">Eroare necunoscută</string> <string name="omnipod_uncertain_failure">Eroare necunoscută</string>
<string name="omnipod_cancelled_old_tbr_failed_to_set_new">S-a anulat vechea bazală temporară, dar nu s-a putut seta o nouă bazală temporară</string> <string name="omnipod_cancelled_old_tbr_failed_to_set_new">S-a anulat vechea bazală temporară, dar nu s-a putut seta o nouă bazală temporară</string>
<string name="omnipod_cmd_set_fake_suspended_tbr">Se setează o bazală temporară falsă deoarece Pod-ul este suspendat</string> <string name="omnipod_cmd_set_fake_suspended_tbr">Se setează o bazală temporară falsă deoarece Pod-ul este suspendat</string>

View file

@ -176,7 +176,7 @@
<string name="omnipod_suspend_delivery_short">Suspend</string> <string name="omnipod_suspend_delivery_short">Suspend</string>
<string name="omnipod_cmd_pair_and_prime">Pair and prime</string> <string name="omnipod_cmd_pair_and_prime">Pair and prime</string>
<string name="omnipod_cmd_fill_cannula_set_basal_profile">Fill cannula and set basal profile</string> <string name="omnipod_cmd_fill_cannula_set_basal_profile">Fill cannula and set basal profile</string>
<string name="omnipod_cmd_get_pulse_log">Get pulse log</string> <string name="omnipod_cmd_read_pulse_log">Read pulse log</string>
<string name="omnipod_uncertain_failure">Uncertain failure</string> <string name="omnipod_uncertain_failure">Uncertain failure</string>
<string name="omnipod_cancelled_old_tbr_failed_to_set_new">Cancelled the old temporary basal, but failed to set new temporary basal</string> <string name="omnipod_cancelled_old_tbr_failed_to_set_new">Cancelled the old temporary basal, but failed to set new temporary basal</string>
<string name="omnipod_cmd_set_fake_suspended_tbr">Set fake temporary basal because the Pod is suspended</string> <string name="omnipod_cmd_set_fake_suspended_tbr">Set fake temporary basal because the Pod is suspended</string>
@ -200,7 +200,7 @@
<string name="omnipod_pod_time_on_pod">Time on Pod</string> <string name="omnipod_pod_time_on_pod">Time on Pod</string>
<string name="omnipod_set_time">Set time</string> <string name="omnipod_set_time">Set time</string>
<string name="omnipod_unknown_custom_command">Unknown custom command: %1$s</string> <string name="omnipod_unknown_custom_command">Unknown custom command: %1$s</string>
<string name="omnipod_failed_to_retrieve_pulse_log">Failed to retrieve Pulse Log</string> <string name="omnipod_failed_to_read_pulse_log">Failed to read Pulse Log</string>
<string name="omnipod_failed_to_refresh_status">Failed to refresh status</string> <string name="omnipod_failed_to_refresh_status">Failed to refresh status</string>
<string name="omnipod_failed_to_acknowledge_alerts">Failed to acknowledge alerts</string> <string name="omnipod_failed_to_acknowledge_alerts">Failed to acknowledge alerts</string>
<string name="omnipod_failed_to_suspend_delivery">Failed to suspend delivery</string> <string name="omnipod_failed_to_suspend_delivery">Failed to suspend delivery</string>