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
This commit is contained in:
timklge 2025-05-04 15:15:23 +02:00 committed by GitHub
parent 21d06aab3a
commit c4a23ce456
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 78 additions and 26 deletions

View File

@ -93,11 +93,10 @@ tasks.register("generateManifest") {
"latestVersionCode" to android.defaultConfig.versionCode, "latestVersionCode" to android.defaultConfig.versionCode,
"developer" to "github.com/timklge", "developer" to "github.com/timklge",
"description" to "Open-source extension that provides headwind direction, wind speed, forecast and other weather data fields.", "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" + "* 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" + "* 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", "* Add OpenWeatherMap support contributed by lockevod\n",
"screenshotUrls" to listOf( "screenshotUrls" to listOf(
"https://github.com/timklge/karoo-headwind/releases/latest/download/preview1.png", "https://github.com/timklge/karoo-headwind/releases/latest/download/preview1.png",

View File

@ -1,6 +1,8 @@
package de.timklge.karooheadwind package de.timklge.karooheadwind
import de.timklge.karooheadwind.datatypes.GpsCoordinates import de.timklge.karooheadwind.datatypes.GpsCoordinates
import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.HardwareType
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json 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 @Serializable
data class HeadwindSettings( data class HeadwindSettings(
@ -74,7 +100,8 @@ data class HeadwindSettings(
val lastUpdateRequested: Long? = null, val lastUpdateRequested: Long? = null,
val showDistanceInForecast: Boolean = true, val showDistanceInForecast: Boolean = true,
val weatherProvider: WeatherDataProvider = WeatherDataProvider.OPEN_METEO, val weatherProvider: WeatherDataProvider = WeatherDataProvider.OPEN_METEO,
val openWeatherMapApiKey: String = "" val openWeatherMapApiKey: String = "",
val refreshRate: RefreshRate = RefreshRate.STANDARD,
){ ){
companion object { companion object {
val defaultSettings = Json.encodeToString(HeadwindSettings()) val defaultSettings = Json.encodeToString(HeadwindSettings())
@ -85,8 +112,6 @@ data class HeadwindSettings(
} }
} }
//added openweathermap.org
@Serializable @Serializable
enum class WeatherDataProvider(val id: String, val label: String) { enum class WeatherDataProvider(val id: String, val label: String) {
OPEN_METEO("open-meteo", "OpenMeteo"), OPEN_METEO("open-meteo", "OpenMeteo"),

View File

@ -194,7 +194,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
context.streamCurrentForecastWeatherData(), context.streamCurrentForecastWeatherData(),
settingsAndProfileStream, settingsAndProfileStream,
context.streamWidgetSettings(), context.streamWidgetSettings(),
karooSystem.getHeadingFlow(context).throttle(60_000L), karooSystem.getHeadingFlow(context).throttle(3 * 60_000L),
karooSystem.streamUpcomingRoute().distinctUntilChanged { old, new -> karooSystem.streamUpcomingRoute().distinctUntilChanged { old, new ->
val oldDistance = old?.distanceAlongRoute val oldDistance = old?.distanceAlongRoute
val newDistance = new?.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 true
if (oldDistance == null || newDistance == null) return@distinctUntilChanged false if (oldDistance == null || newDistance == null) return@distinctUntilChanged false
abs(oldDistance - newDistance) < 500 abs(oldDistance - newDistance) < 1_000
} }
) { weatherData, settings, widgetSettings, heading, upcomingRoute -> ) { weatherData, settings, widgetSettings, heading, upcomingRoute ->
StreamData( StreamData(

View File

@ -13,12 +13,14 @@ import de.timklge.karooheadwind.WindDirectionIndicatorSetting
import de.timklge.karooheadwind.getRelativeHeadingFlow import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.streamCurrentWeatherData import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.throttle
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.Emitter import io.hammerhead.karooext.internal.Emitter
import io.hammerhead.karooext.internal.ViewEmitter import io.hammerhead.karooext.internal.ViewEmitter
import io.hammerhead.karooext.models.DataPoint import io.hammerhead.karooext.models.DataPoint
import io.hammerhead.karooext.models.DataType import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.HardwareType
import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.StreamState
import io.hammerhead.karooext.models.UpdateGraphicConfig import io.hammerhead.karooext.models.UpdateGraphicConfig
import io.hammerhead.karooext.models.ViewConfig import io.hammerhead.karooext.models.ViewConfig
@ -29,6 +31,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -139,7 +142,9 @@ class HeadwindDirectionDataType(
} }
val viewJob = CoroutineScope(Dispatchers.IO).launch { 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") Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
val errorCode = streamData.bearing.let { if(it < 0) it.toInt() else null } 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_NO_WEATHER_DATA = -2
const val ERROR_APP_NOT_SET_UP = -3 const val ERROR_APP_NOT_SET_UP = -3
} }
} }
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
}
}

View File

@ -23,6 +23,7 @@ import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.ViewEmitter import io.hammerhead.karooext.internal.ViewEmitter
@ -73,14 +74,7 @@ class TailwindAndRideSpeedDataType(
) : DataTypeImpl("karoo-headwind", "tailwind-and-ride-speed") { ) : DataTypeImpl("karoo-headwind", "tailwind-and-ride-speed") {
private val glance = GlanceRemoteViews() private val glance = GlanceRemoteViews()
data class StreamData(val headingResponse: HeadingResponse, private fun previewFlow(profileFlow: Flow<UserProfile>): Flow<StreamData> {
val absoluteWindDirection: Double?,
val windSpeed: Double?,
val settings: HeadwindSettings?,
val rideSpeed: Double? = null,
val isImperial: Boolean? = null)
private fun previewFlow(profileFlow: Flow<UserProfile>): Flow<de.timklge.karooheadwind.datatypes.TailwindDataType.StreamData> {
return flow { return flow {
val profile = profileFlow.first() val profile = profileFlow.first()
@ -137,7 +131,8 @@ class TailwindAndRideSpeedDataType(
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
emitter.onNext(ShowCustomStreamState("", null)) 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") Log.d(KarooHeadwindExtension.TAG, "Updating headwind direction view")
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff

View File

@ -19,6 +19,7 @@ import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow import de.timklge.karooheadwind.streamDataFlow
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
import de.timklge.karooheadwind.streamUserProfile import de.timklge.karooheadwind.streamUserProfile
import de.timklge.karooheadwind.throttle
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.ViewEmitter import io.hammerhead.karooext.internal.ViewEmitter
@ -115,7 +116,8 @@ class TailwindDataType(
val viewJob = CoroutineScope(Dispatchers.IO).launch { val viewJob = CoroutineScope(Dispatchers.IO).launch {
emitter.onNext(ShowCustomStreamState("", null)) 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") Log.d(KarooHeadwindExtension.TAG, "Updating tailwind direction view")
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff

View File

@ -39,11 +39,11 @@ import androidx.compose.ui.window.Dialog
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import de.timklge.karooheadwind.HeadwindSettings import de.timklge.karooheadwind.HeadwindSettings
import de.timklge.karooheadwind.KarooHeadwindExtension import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.RefreshRate
import de.timklge.karooheadwind.RoundLocationSetting import de.timklge.karooheadwind.RoundLocationSetting
import de.timklge.karooheadwind.WeatherDataProvider import de.timklge.karooheadwind.WeatherDataProvider
import de.timklge.karooheadwind.WindDirectionIndicatorSetting import de.timklge.karooheadwind.WindDirectionIndicatorSetting
import de.timklge.karooheadwind.WindDirectionIndicatorTextSetting import de.timklge.karooheadwind.WindDirectionIndicatorTextSetting
import de.timklge.karooheadwind.WindUnit
import de.timklge.karooheadwind.datatypes.GpsCoordinates import de.timklge.karooheadwind.datatypes.GpsCoordinates
import de.timklge.karooheadwind.saveSettings import de.timklge.karooheadwind.saveSettings
import de.timklge.karooheadwind.streamSettings import de.timklge.karooheadwind.streamSettings
@ -63,6 +63,7 @@ fun SettingsScreen(onFinish: () -> Unit) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val karooSystem = remember { KarooSystemService(ctx) } val karooSystem = remember { KarooSystemService(ctx) }
var refreshRateSetting by remember { mutableStateOf(RefreshRate.STANDARD) }
var selectedWindDirectionIndicatorTextSetting by remember { var selectedWindDirectionIndicatorTextSetting by remember {
mutableStateOf( mutableStateOf(
WindDirectionIndicatorTextSetting.HEADWIND_SPEED WindDirectionIndicatorTextSetting.HEADWIND_SPEED
@ -73,6 +74,7 @@ fun SettingsScreen(onFinish: () -> Unit) {
WindDirectionIndicatorSetting.HEADWIND_DIRECTION WindDirectionIndicatorSetting.HEADWIND_DIRECTION
) )
} }
var selectedRoundLocationSetting by remember { mutableStateOf(RoundLocationSetting.KM_3) } var selectedRoundLocationSetting by remember { mutableStateOf(RoundLocationSetting.KM_3) }
var forecastKmPerHour by remember { mutableStateOf("20") } var forecastKmPerHour by remember { mutableStateOf("20") }
var forecastMilesPerHour by remember { mutableStateOf("12") } var forecastMilesPerHour by remember { mutableStateOf("12") }
@ -93,6 +95,7 @@ fun SettingsScreen(onFinish: () -> Unit) {
showDistanceInForecast = settings.showDistanceInForecast showDistanceInForecast = settings.showDistanceInForecast
selectedWeatherProvider = settings.weatherProvider selectedWeatherProvider = settings.weatherProvider
openWeatherMapApiKey = settings.openWeatherMapApiKey openWeatherMapApiKey = settings.openWeatherMapApiKey
refreshRateSetting = settings.refreshRate
} }
} }
@ -120,7 +123,8 @@ fun SettingsScreen(onFinish: () -> Unit) {
forecastedKmPerHour = forecastKmPerHour.toIntOrNull()?.coerceIn(5, 50) ?: 20, forecastedKmPerHour = forecastKmPerHour.toIntOrNull()?.coerceIn(5, 50) ?: 20,
showDistanceInForecast = showDistanceInForecast, showDistanceInForecast = showDistanceInForecast,
weatherProvider = selectedWeatherProvider, weatherProvider = selectedWeatherProvider,
openWeatherMapApiKey = openWeatherMapApiKey openWeatherMapApiKey = openWeatherMapApiKey,
refreshRate = refreshRateSetting,
) )
saveSettings(ctx, newSettings) saveSettings(ctx, newSettings)
@ -147,15 +151,26 @@ fun SettingsScreen(onFinish: () -> Unit) {
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp) .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 = val windDirectionIndicatorSettingDropdownOptions =
WindDirectionIndicatorSetting.entries.toList() WindDirectionIndicatorSetting.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) }
.map { unit -> DropdownOption(unit.id, unit.label) }
val windDirectionIndicatorSettingSelection by remember(selectedWindDirectionIndicatorSetting) { val windDirectionIndicatorSettingSelection by remember(selectedWindDirectionIndicatorSetting) {
mutableStateOf(windDirectionIndicatorSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorSetting.id }!!) mutableStateOf(windDirectionIndicatorSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorSetting.id }!!)
} }
Dropdown( Dropdown(
label = "Wind direction indicator", label = "Wind Direction Indicator",
options = windDirectionIndicatorSettingDropdownOptions, options = windDirectionIndicatorSettingDropdownOptions,
selected = windDirectionIndicatorSettingSelection selected = windDirectionIndicatorSettingSelection
) { selectedOption -> ) { selectedOption ->
@ -172,7 +187,7 @@ fun SettingsScreen(onFinish: () -> Unit) {
mutableStateOf(windDirectionIndicatorTextSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorTextSetting.id }!!) mutableStateOf(windDirectionIndicatorTextSettingDropdownOptions.find { option -> option.id == selectedWindDirectionIndicatorTextSetting.id }!!)
} }
Dropdown( Dropdown(
label = "Text on headwind indicator", label = "Text on Headwind Indicator",
options = windDirectionIndicatorTextSettingDropdownOptions, options = windDirectionIndicatorTextSettingDropdownOptions,
selected = windDirectionIndicatorTextSettingSelection selected = windDirectionIndicatorTextSettingSelection
) { selectedOption -> ) { selectedOption ->