From c4a23ce4565336e0a55a43766b985207d5b82e50 Mon Sep 17 00:00:00 2001 From: timklge <2026103+timklge@users.noreply.github.com> Date: Sun, 4 May 2025 15:15:23 +0200 Subject: [PATCH] Reduce refresh rate on K2, add refresh rate setting (#117) * Reduce refresh rate on K2, add refresh rate setting * Set default k2 update interval to 2s --- app/build.gradle.kts | 5 ++- .../timklge/karooheadwind/HeadwindSettings.kt | 31 +++++++++++++++++-- .../datatypes/ForecastDataType.kt | 4 +-- .../datatypes/HeadwindDirectionDataType.kt | 20 ++++++++++-- .../datatypes/TailwindAndRideSpeedDataType.kt | 13 +++----- .../datatypes/TailwindDataType.kt | 4 ++- .../karooheadwind/screens/SettingsScreen.kt | 27 ++++++++++++---- 7 files changed, 78 insertions(+), 26 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 505b0fc..d1b7087 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -93,11 +93,10 @@ tasks.register("generateManifest") { "latestVersionCode" to android.defaultConfig.versionCode, "developer" to "github.com/timklge", "description" to "Open-source extension that provides headwind direction, wind speed, forecast and other weather data fields.", - "releaseNotes" to "* Fix weather data download from Open-Meteo via iOS companion app (thx @keefar!)\n" + + "releaseNotes" to "* Reduce refresh rate on K2, add refresh rate setting\n" + + "* Fix weather data download from Open-Meteo via iOS companion app (thx @keefar!)\n" + "* Remove custom wind speed unit setting and always use imperial / metric as set in profile\n" + "* Add relative grade, relative elevation gain data fields\n" + - "* Fix precipitation forecast field\n" + - "* Interpolate between forecasted and current weather data\n" + "* Add OpenWeatherMap support contributed by lockevod\n", "screenshotUrls" to listOf( "https://github.com/timklge/karoo-headwind/releases/latest/download/preview1.png", diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/HeadwindSettings.kt b/app/src/main/kotlin/de/timklge/karooheadwind/HeadwindSettings.kt index af5e632..7ef6ff7 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/HeadwindSettings.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/HeadwindSettings.kt @@ -1,6 +1,8 @@ package de.timklge.karooheadwind import de.timklge.karooheadwind.datatypes.GpsCoordinates +import io.hammerhead.karooext.KarooSystemService +import io.hammerhead.karooext.models.HardwareType import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -62,6 +64,30 @@ data class HeadwindStats( } +enum class RefreshRate(val id: String, val k2Ms: Long, val k3Ms: Long) { + FAST("fast", 1_000L, 500L), + STANDARD("medium", 2_000L, 1_000L), + SLOW("slow", 5_000L, 3_000L), + MINIMUM("minimum", 10_000L, 10_000L); + + fun getDescription(karooSystemService: KarooSystemService): String { + return if (karooSystemService.hardwareType == HardwareType.K2) { + when (this) { + FAST -> "Fast (1s)" + STANDARD -> "Standard (2s)" + SLOW -> "Slow (5s)" + MINIMUM -> "Minimum (10s)" + } + } else { + when (this) { + FAST -> "Fastest" + STANDARD -> "Standard (1s)" + SLOW -> "Slow (3s)" + MINIMUM -> "Minimum (10s)" + } + } + } +} @Serializable data class HeadwindSettings( @@ -74,7 +100,8 @@ data class HeadwindSettings( val lastUpdateRequested: Long? = null, val showDistanceInForecast: Boolean = true, val weatherProvider: WeatherDataProvider = WeatherDataProvider.OPEN_METEO, - val openWeatherMapApiKey: String = "" + val openWeatherMapApiKey: String = "", + val refreshRate: RefreshRate = RefreshRate.STANDARD, ){ companion object { val defaultSettings = Json.encodeToString(HeadwindSettings()) @@ -85,8 +112,6 @@ data class HeadwindSettings( } } -//added openweathermap.org - @Serializable enum class WeatherDataProvider(val id: String, val label: String) { OPEN_METEO("open-meteo", "OpenMeteo"), diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/ForecastDataType.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/ForecastDataType.kt index ced984d..3da7aea 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/ForecastDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/ForecastDataType.kt @@ -194,7 +194,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ context.streamCurrentForecastWeatherData(), settingsAndProfileStream, context.streamWidgetSettings(), - karooSystem.getHeadingFlow(context).throttle(60_000L), + karooSystem.getHeadingFlow(context).throttle(3 * 60_000L), karooSystem.streamUpcomingRoute().distinctUntilChanged { old, new -> val oldDistance = old?.distanceAlongRoute val newDistance = new?.distanceAlongRoute @@ -202,7 +202,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ if (oldDistance == null && newDistance == null) return@distinctUntilChanged true if (oldDistance == null || newDistance == null) return@distinctUntilChanged false - abs(oldDistance - newDistance) < 500 + abs(oldDistance - newDistance) < 1_000 } ) { weatherData, settings, widgetSettings, heading, upcomingRoute -> StreamData( diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt index 845fce8..6b3d446 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/HeadwindDirectionDataType.kt @@ -13,12 +13,14 @@ import de.timklge.karooheadwind.WindDirectionIndicatorSetting import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamSettings +import de.timklge.karooheadwind.throttle import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.internal.Emitter import io.hammerhead.karooext.internal.ViewEmitter import io.hammerhead.karooext.models.DataPoint import io.hammerhead.karooext.models.DataType +import io.hammerhead.karooext.models.HardwareType import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.UpdateGraphicConfig import io.hammerhead.karooext.models.ViewConfig @@ -29,6 +31,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch import kotlin.math.roundToInt @@ -139,7 +142,9 @@ class HeadwindDirectionDataType( } val viewJob = CoroutineScope(Dispatchers.IO).launch { - flow.collect { streamData -> + val refreshRate = karooSystem.getRefreshRateInMilliseconds(context) + + flow.throttle(refreshRate).collect { streamData -> Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view") val errorCode = streamData.bearing.let { if(it < 0) it.toInt() else null } @@ -177,4 +182,15 @@ class HeadwindDirectionDataType( const val ERROR_NO_WEATHER_DATA = -2 const val ERROR_APP_NOT_SET_UP = -3 } -} \ No newline at end of file +} + +suspend fun KarooSystemService.getRefreshRateInMilliseconds(context: Context): Long { + val refreshRate = context.streamSettings(this).first().refreshRate + val isK2 = hardwareType == HardwareType.K2 + + return if (isK2){ + refreshRate.k2Ms + } else { + refreshRate.k3Ms + } +} diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindAndRideSpeedDataType.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindAndRideSpeedDataType.kt index 2fff84b..9bfff90 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindAndRideSpeedDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindAndRideSpeedDataType.kt @@ -23,6 +23,7 @@ import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamDataFlow import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamUserProfile +import de.timklge.karooheadwind.throttle import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.internal.ViewEmitter @@ -73,14 +74,7 @@ class TailwindAndRideSpeedDataType( ) : DataTypeImpl("karoo-headwind", "tailwind-and-ride-speed") { private val glance = GlanceRemoteViews() - data class StreamData(val headingResponse: HeadingResponse, - val absoluteWindDirection: Double?, - val windSpeed: Double?, - val settings: HeadwindSettings?, - val rideSpeed: Double? = null, - val isImperial: Boolean? = null) - - private fun previewFlow(profileFlow: Flow): Flow { + private fun previewFlow(profileFlow: Flow): Flow { return flow { val profile = profileFlow.first() @@ -137,7 +131,8 @@ class TailwindAndRideSpeedDataType( val viewJob = CoroutineScope(Dispatchers.IO).launch { emitter.onNext(ShowCustomStreamState("", null)) - flow.collect { streamData -> + val refreshRate = karooSystem.getRefreshRateInMilliseconds(context) + flow.throttle(refreshRate).collect { streamData -> Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view") val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindDataType.kt b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindDataType.kt index a84f81b..28b4254 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindDataType.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/datatypes/TailwindDataType.kt @@ -19,6 +19,7 @@ import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamDataFlow import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamUserProfile +import de.timklge.karooheadwind.throttle import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.internal.ViewEmitter @@ -115,7 +116,8 @@ class TailwindDataType( val viewJob = CoroutineScope(Dispatchers.IO).launch { emitter.onNext(ShowCustomStreamState("", null)) - flow.collect { streamData -> + val refreshRate = karooSystem.getRefreshRateInMilliseconds(context) + flow.throttle(refreshRate).collect { streamData -> Log.d(KarooHeadwindExtension.TAG, "Updating tailwind direction view") val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff diff --git a/app/src/main/kotlin/de/timklge/karooheadwind/screens/SettingsScreen.kt b/app/src/main/kotlin/de/timklge/karooheadwind/screens/SettingsScreen.kt index dba5b5e..e6ffae8 100644 --- a/app/src/main/kotlin/de/timklge/karooheadwind/screens/SettingsScreen.kt +++ b/app/src/main/kotlin/de/timklge/karooheadwind/screens/SettingsScreen.kt @@ -39,11 +39,11 @@ import androidx.compose.ui.window.Dialog import androidx.lifecycle.compose.collectAsStateWithLifecycle import de.timklge.karooheadwind.HeadwindSettings import de.timklge.karooheadwind.KarooHeadwindExtension +import de.timklge.karooheadwind.RefreshRate import de.timklge.karooheadwind.RoundLocationSetting import de.timklge.karooheadwind.WeatherDataProvider import de.timklge.karooheadwind.WindDirectionIndicatorSetting import de.timklge.karooheadwind.WindDirectionIndicatorTextSetting -import de.timklge.karooheadwind.WindUnit import de.timklge.karooheadwind.datatypes.GpsCoordinates import de.timklge.karooheadwind.saveSettings import de.timklge.karooheadwind.streamSettings @@ -63,6 +63,7 @@ fun SettingsScreen(onFinish: () -> Unit) { val coroutineScope = rememberCoroutineScope() val karooSystem = remember { KarooSystemService(ctx) } + var refreshRateSetting by remember { mutableStateOf(RefreshRate.STANDARD) } var selectedWindDirectionIndicatorTextSetting by remember { mutableStateOf( WindDirectionIndicatorTextSetting.HEADWIND_SPEED @@ -73,6 +74,7 @@ fun SettingsScreen(onFinish: () -> Unit) { WindDirectionIndicatorSetting.HEADWIND_DIRECTION ) } + var selectedRoundLocationSetting by remember { mutableStateOf(RoundLocationSetting.KM_3) } var forecastKmPerHour by remember { mutableStateOf("20") } var forecastMilesPerHour by remember { mutableStateOf("12") } @@ -93,6 +95,7 @@ fun SettingsScreen(onFinish: () -> Unit) { showDistanceInForecast = settings.showDistanceInForecast selectedWeatherProvider = settings.weatherProvider openWeatherMapApiKey = settings.openWeatherMapApiKey + refreshRateSetting = settings.refreshRate } } @@ -120,7 +123,8 @@ fun SettingsScreen(onFinish: () -> Unit) { forecastedKmPerHour = forecastKmPerHour.toIntOrNull()?.coerceIn(5, 50) ?: 20, showDistanceInForecast = showDistanceInForecast, weatherProvider = selectedWeatherProvider, - openWeatherMapApiKey = openWeatherMapApiKey + openWeatherMapApiKey = openWeatherMapApiKey, + refreshRate = refreshRateSetting, ) saveSettings(ctx, newSettings) @@ -147,15 +151,26 @@ fun SettingsScreen(onFinish: () -> Unit) { .verticalScroll(rememberScrollState()) .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp) ) { + val refreshRateDropdownOptions = RefreshRate.entries.toList().map { unit -> DropdownOption(unit.id, unit.getDescription(karooSystem)) } + val refreshRateSelection by remember(refreshRateSetting) { + mutableStateOf(refreshRateDropdownOptions.find { option -> option.id == refreshRateSetting.id }!!) + } + Dropdown( + label = "Refresh Rate", + options = refreshRateDropdownOptions, + selected = refreshRateSelection + ) { selectedOption -> + refreshRateSetting = + RefreshRate.entries.find { unit -> unit.id == selectedOption.id }!! + } val windDirectionIndicatorSettingDropdownOptions = - WindDirectionIndicatorSetting.entries.toList() - .map { unit -> DropdownOption(unit.id, unit.label) } + WindDirectionIndicatorSetting.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } val windDirectionIndicatorSettingSelection by remember(selectedWindDirectionIndicatorSetting) { mutableStateOf(windDirectionIndicatorSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorSetting.id }!!) } Dropdown( - label = "Wind direction indicator", + label = "Wind Direction Indicator", options = windDirectionIndicatorSettingDropdownOptions, selected = windDirectionIndicatorSettingSelection ) { selectedOption -> @@ -172,7 +187,7 @@ fun SettingsScreen(onFinish: () -> Unit) { mutableStateOf(windDirectionIndicatorTextSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorTextSetting.id }!!) } Dropdown( - label = "Text on headwind indicator", + label = "Text on Headwind Indicator", options = windDirectionIndicatorTextSettingDropdownOptions, selected = windDirectionIndicatorTextSettingSelection ) { selectedOption ->