Add rolling average / smoothing setting to reminder ui for power trigger (#48)
* Add smooth setting enum # Conflicts: # app/src/main/kotlin/de/timklge/karooreminder/KarooReminderExtension.kt * Add rolling average setting to reminder ui * Use smoothing for power trigger * Update change log
This commit is contained in:
parent
24cada2cad
commit
04820cf120
@ -71,9 +71,7 @@ tasks.register("generateManifest") {
|
||||
"latestVersionCode" to android.defaultConfig.versionCode,
|
||||
"developer" to "timklge",
|
||||
"description" to "Shows in-ride alerts after a given time interval, distance or HR / power / speed / cadence out of range",
|
||||
"releaseNotes" to "* Only show reminders while riding\n" +
|
||||
"* Limit reminder title length in list\n" +
|
||||
"* Use imperial units for temperature and tire pressure if selected\n"
|
||||
"releaseNotes" to "* Add rolling average setting for power triggers"
|
||||
)
|
||||
|
||||
val gson = groovy.json.JsonBuilder(manifest).toPrettyString()
|
||||
|
||||
@ -5,8 +5,6 @@ import android.media.MediaPlayer
|
||||
import android.util.Log
|
||||
import de.timklge.karooreminder.screens.Reminder
|
||||
import de.timklge.karooreminder.screens.ReminderBeepPattern
|
||||
import de.timklge.karooreminder.screens.defaultReminders
|
||||
import de.timklge.karooreminder.screens.preferencesKey
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.extension.KarooExtension
|
||||
import io.hammerhead.karooext.models.DataType
|
||||
@ -34,8 +32,19 @@ import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
enum class SmoothSetting(val label: String) {
|
||||
NONE("None"),
|
||||
SMOOTH_3S("3 seconds"),
|
||||
SMOOTH_10S("10 seconds"),
|
||||
SMOOTH_30S("30 seconds"),
|
||||
SMOOTH_20M("20 minutes"),
|
||||
SMOOTH_60M("60 minutes"),
|
||||
SMOOTH_LAP("Lap"),
|
||||
SMOOTH_RIDE("Ride");
|
||||
}
|
||||
|
||||
enum class ReminderTrigger(val id: String, val label: String) {
|
||||
ELAPSED_TIME("elapsed_time", "Elapsed Time"),
|
||||
@ -92,6 +101,45 @@ enum class ReminderTrigger(val id: String, val label: String) {
|
||||
this == REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED || this == REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED ||
|
||||
this == AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED || this == AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED ||
|
||||
this == GRADIENT_LIMIT_MAXIMUM_EXCEEDED || this == GRADIENT_LIMIT_MINIMUM_EXCEEDED
|
||||
|
||||
fun getSmoothedDataType(smoothSetting: SmoothSetting): String {
|
||||
return when(this) {
|
||||
POWER_LIMIT_MAXIMUM_EXCEEDED, POWER_LIMIT_MINIMUM_EXCEEDED -> {
|
||||
when (smoothSetting) {
|
||||
SmoothSetting.NONE -> DataType.Type.POWER
|
||||
SmoothSetting.SMOOTH_3S -> DataType.Type.SMOOTHED_3S_AVERAGE_POWER
|
||||
SmoothSetting.SMOOTH_10S -> DataType.Type.SMOOTHED_10S_AVERAGE_POWER
|
||||
SmoothSetting.SMOOTH_30S -> DataType.Type.SMOOTHED_30S_AVERAGE_POWER
|
||||
SmoothSetting.SMOOTH_20M -> DataType.Type.SMOOTHED_20M_AVERAGE_POWER
|
||||
SmoothSetting.SMOOTH_60M -> DataType.Type.SMOOTHED_1HR_AVERAGE_POWER
|
||||
SmoothSetting.SMOOTH_LAP -> DataType.Type.POWER_LAP
|
||||
SmoothSetting.SMOOTH_RIDE -> DataType.Type.AVERAGE_POWER
|
||||
}
|
||||
}
|
||||
|
||||
else -> getDataType()
|
||||
}
|
||||
}
|
||||
|
||||
fun hasSmoothedDataTypes(): Boolean {
|
||||
return this == POWER_LIMIT_MAXIMUM_EXCEEDED || this == POWER_LIMIT_MINIMUM_EXCEEDED
|
||||
}
|
||||
|
||||
fun getDataType(): String {
|
||||
return when (this) {
|
||||
ReminderTrigger.HR_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.HR_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.HEART_RATE
|
||||
POWER_LIMIT_MAXIMUM_EXCEEDED, POWER_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_POWER
|
||||
ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_SPEED
|
||||
ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_CADENCE
|
||||
ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.CORE_TEMP
|
||||
ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TIRE_PRESSURE_FRONT
|
||||
ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TIRE_PRESSURE_REAR
|
||||
ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.ELEVATION_GRADE
|
||||
ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TEMPERATURE
|
||||
|
||||
ReminderTrigger.DISTANCE, ReminderTrigger.ELAPSED_TIME, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $this")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Flow<Int>.allIntermediateInts(): Flow<Int> = flow {
|
||||
@ -251,9 +299,11 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
|
||||
)
|
||||
|
||||
intervalTriggers.forEach { trigger ->
|
||||
if (reminders.any { it.trigger == trigger }){
|
||||
val job = startRangeExceededJob(trigger)
|
||||
triggerJobs.add(job)
|
||||
SmoothSetting.entries.forEach { smoothSetting ->
|
||||
if (reminders.any { it.trigger == trigger && it.smoothSetting == smoothSetting }){
|
||||
val job = startRangeExceededJob(trigger, smoothSetting)
|
||||
triggerJobs.add(job)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,25 +341,13 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
|
||||
}
|
||||
}
|
||||
|
||||
private fun startRangeExceededJob(triggerType: ReminderTrigger): Job {
|
||||
private fun startRangeExceededJob(triggerType: ReminderTrigger, smoothSetting: SmoothSetting): Job {
|
||||
return CoroutineScope(Dispatchers.IO).launch {
|
||||
val preferences = streamPreferences()
|
||||
|
||||
val dataType = when (triggerType) {
|
||||
ReminderTrigger.HR_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.HR_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.HEART_RATE
|
||||
ReminderTrigger.POWER_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.POWER_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_POWER
|
||||
ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_SPEED
|
||||
ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_CADENCE
|
||||
ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.CORE_TEMP
|
||||
ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TIRE_PRESSURE_FRONT
|
||||
ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TIRE_PRESSURE_REAR
|
||||
ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.ELEVATION_GRADE
|
||||
ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TEMPERATURE
|
||||
Log.i(TAG, "Starting range exceeded job for trigger $triggerType with smooth setting $smoothSetting")
|
||||
|
||||
ReminderTrigger.DISTANCE, ReminderTrigger.ELAPSED_TIME, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $triggerType")
|
||||
}
|
||||
|
||||
val valueStream = karooSystem.streamDataFlow(dataType)
|
||||
val valueStream = karooSystem.streamDataFlow(triggerType.getSmoothedDataType(smoothSetting))
|
||||
.mapNotNull {
|
||||
val dataPoint = (it as? StreamState.Streaming)?.dataPoint
|
||||
|
||||
@ -394,13 +432,7 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
|
||||
ReminderTrigger.ELAPSED_TIME, ReminderTrigger.DISTANCE, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $triggerType")
|
||||
}
|
||||
|
||||
val result = reminder.isActive && reminder.trigger == triggerType && triggerIsMet
|
||||
/* if (result){
|
||||
Log.i(TAG, "Triggered range reminder: ${reminder.name} (${triggerType}): actual value $actualValue, threshold $triggerThreshold")
|
||||
} else if(reminder.trigger == triggerType && reminder.isActive) {
|
||||
Log.i(TAG, "Not triggered range reminder: ${reminder.name} (${triggerType}): actual value $actualValue, threshold $triggerThreshold")
|
||||
} */
|
||||
result
|
||||
reminder.isActive && reminder.trigger == triggerType && triggerIsMet
|
||||
}
|
||||
|
||||
triggered
|
||||
@ -408,6 +440,9 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
|
||||
.filterNotNull()
|
||||
.filter { it.isNotEmpty() }
|
||||
.throttle(1_000 * 60) // At most once every minute
|
||||
.onCompletion {
|
||||
Log.i(TAG, "Range exceeded job for trigger $triggerType with smooth setting $smoothSetting completed")
|
||||
}
|
||||
.collectLatest { reminders ->
|
||||
reminders.forEach { reminder ->
|
||||
Log.d(TAG, "Dispatching reminder: ${reminder.name}")
|
||||
|
||||
@ -62,6 +62,7 @@ import com.maxkeppeler.sheets.color.models.MultipleColors
|
||||
import com.maxkeppeler.sheets.color.models.SingleColor
|
||||
import de.timklge.karooreminder.R
|
||||
import de.timklge.karooreminder.ReminderTrigger
|
||||
import de.timklge.karooreminder.SmoothSetting
|
||||
import de.timklge.karooreminder.streamUserProfile
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.HardwareType
|
||||
@ -92,11 +93,13 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
reminder.interval.toString()
|
||||
})
|
||||
}
|
||||
var smoothSetting by remember { mutableStateOf(reminder.smoothSetting) }
|
||||
var isActive by remember { mutableStateOf(reminder.isActive) }
|
||||
var autoDismiss by remember { mutableStateOf(reminder.isAutoDismiss) }
|
||||
var deleteDialogVisible by remember { mutableStateOf(false) }
|
||||
var toneDialogVisible by remember { mutableStateOf(false) }
|
||||
var triggerDialogVisible by remember { mutableStateOf(false) }
|
||||
var smoothSettingDialogVisible by remember { mutableStateOf(false) }
|
||||
var selectedTone by remember { mutableStateOf(reminder.tone) }
|
||||
var autoDismissSeconds by remember { mutableStateOf(reminder.autoDismissSeconds.toString()) }
|
||||
var selectedTrigger by remember { mutableStateOf(reminder.trigger) }
|
||||
@ -111,6 +114,7 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
text = text,
|
||||
displayForegroundColor = selectedColor,
|
||||
isActive = isActive,
|
||||
smoothSetting = smoothSetting,
|
||||
trigger = selectedTrigger,
|
||||
isAutoDismiss = autoDismiss, tone = selectedTone, autoDismissSeconds = autoDismissSeconds.toIntOrNull() ?: 15)
|
||||
}
|
||||
@ -172,6 +176,18 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
singleLine = true
|
||||
)
|
||||
|
||||
if (selectedTrigger.hasSmoothedDataTypes()){
|
||||
FilledTonalButton(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(60.dp), onClick = {
|
||||
smoothSettingDialogVisible = true
|
||||
}) {
|
||||
Icon(Icons.Default.Build, contentDescription = "Change Smooth Setting", modifier = Modifier.size(20.dp))
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
Text("Average: ${smoothSetting.label}")
|
||||
}
|
||||
}
|
||||
|
||||
ColorDialog(
|
||||
state = colorDialogState,
|
||||
selection = ColorSelection(
|
||||
@ -338,6 +354,41 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
}
|
||||
}
|
||||
|
||||
if (smoothSettingDialogVisible) {
|
||||
Dialog(onDismissRequest = { smoothSettingDialogVisible = false }) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp),
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
) {
|
||||
Column(modifier = Modifier
|
||||
.padding(5.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
|
||||
SmoothSetting.entries.forEach { setting ->
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
smoothSetting = setting
|
||||
smoothSettingDialogVisible = false
|
||||
}, verticalAlignment = Alignment.CenterVertically) {
|
||||
RadioButton(selected = smoothSetting == setting, onClick = {
|
||||
smoothSetting = setting
|
||||
smoothSettingDialogVisible = false
|
||||
})
|
||||
Text(
|
||||
text = setting.label,
|
||||
modifier = Modifier.padding(start = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toneDialogVisible){
|
||||
Dialog(onDismissRequest = { toneDialogVisible = false }) {
|
||||
Card(
|
||||
|
||||
@ -5,6 +5,7 @@ import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import de.timklge.karooreminder.R
|
||||
import de.timklge.karooreminder.ReminderTrigger
|
||||
import de.timklge.karooreminder.SmoothSetting
|
||||
import io.hammerhead.karooext.models.PlayBeepPattern
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
@ -131,6 +132,8 @@ class Reminder(val id: Int, var name: String,
|
||||
var interval: Int? = null,
|
||||
/** Trigger value used by temperature, gradient, tire pressure triggers */
|
||||
var intervalFloat: Double? = null,
|
||||
/** Smooth interval used by power, speed triggers */
|
||||
var smoothSetting: SmoothSetting = SmoothSetting.SMOOTH_3S,
|
||||
var text: String,
|
||||
var displayForegroundColor: ReminderColor? = null,
|
||||
@Deprecated("Use displayForegroundColor instead")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user