fix #6: Add separate headwind speed data field
This commit is contained in:
parent
0830674d62
commit
767e5fe769
@ -20,6 +20,7 @@ import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
@ -32,6 +33,7 @@ import kotlinx.coroutines.time.debounce
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.time.Duration
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
@ -142,8 +144,42 @@ suspend fun KarooSystemService.makeOpenMeteoHttpRequest(gpsCoordinates: GpsCoord
|
||||
}.single()
|
||||
}
|
||||
|
||||
fun signedAngleDifference(angle1: Double, angle2: Double): Double {
|
||||
val a1 = angle1 % 360
|
||||
val a2 = angle2 % 360
|
||||
var diff = abs(a1 - a2)
|
||||
|
||||
val sign = if (a1 < a2) {
|
||||
if (diff > 180.0) -1 else 1
|
||||
} else {
|
||||
if (diff > 180.0) 1 else -1
|
||||
}
|
||||
|
||||
if (diff > 180.0) {
|
||||
diff = 360.0 - diff
|
||||
}
|
||||
|
||||
return sign * diff
|
||||
}
|
||||
|
||||
fun KarooSystemService.getRelativeHeadingFlow(context: Context): Flow<Double> {
|
||||
val currentWeatherData = context.streamCurrentWeatherData()
|
||||
|
||||
return getHeadingFlow()
|
||||
.filter { it >= 0 }
|
||||
.combine(currentWeatherData) { cardinalDirection, data -> cardinalDirection to data }
|
||||
.map { (cardinalDirection, data) ->
|
||||
val bearing = cardinalDirection * 45.0
|
||||
val windBearing = data.current.windDirection + 180
|
||||
val diff = (signedAngleDifference(bearing, windBearing) + 360) % 360
|
||||
Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff")
|
||||
|
||||
diff
|
||||
}
|
||||
}
|
||||
|
||||
fun KarooSystemService.getHeadingFlow(): Flow<Int> {
|
||||
return flowOf(2)
|
||||
// return flowOf(2)
|
||||
|
||||
return streamDataFlow(DataType.Type.HEADING)
|
||||
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue }
|
||||
@ -153,7 +189,7 @@ fun KarooSystemService.getHeadingFlow(): Flow<Int> {
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
fun KarooSystemService.getGpsCoordinateFlow(): Flow<GpsCoordinates> {
|
||||
return flowOf(GpsCoordinates(52.5164069,13.3784))
|
||||
// return flowOf(GpsCoordinates(52.5164069,13.3784))
|
||||
|
||||
return streamDataFlow("TYPE_LOCATION_ID")
|
||||
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.values }
|
||||
|
||||
@ -8,8 +8,8 @@ import de.timklge.karooheadwind.datatypes.RelativeHumidityDataType
|
||||
import de.timklge.karooheadwind.datatypes.SurfacePressureDataType
|
||||
import de.timklge.karooheadwind.datatypes.WindDirectionDataType
|
||||
import de.timklge.karooheadwind.datatypes.WindGustsDataType
|
||||
import de.timklge.karooheadwind.datatypes.WindSpeedDataType
|
||||
import de.timklge.karooheadwind.datatypes.RelativeWindDirectionDataType
|
||||
import de.timklge.karooheadwind.datatypes.HeadwindSpeedDataType
|
||||
import de.timklge.karooheadwind.datatypes.HeadwindDirectionDataType
|
||||
import de.timklge.karooheadwind.datatypes.WeatherDataType
|
||||
import de.timklge.karooheadwind.screens.HeadwindStats
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
@ -40,11 +40,12 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.0.0-beta2") {
|
||||
|
||||
override val types by lazy {
|
||||
listOf(
|
||||
RelativeWindDirectionDataType(karooSystem, applicationContext),
|
||||
HeadwindDirectionDataType(karooSystem, applicationContext),
|
||||
HeadwindSpeedDataType(karooSystem, applicationContext),
|
||||
WeatherDataType(karooSystem, applicationContext),
|
||||
HeadwindSpeedDataType(karooSystem, applicationContext),
|
||||
RelativeHumidityDataType(applicationContext),
|
||||
CloudCoverDataType(applicationContext),
|
||||
WindSpeedDataType(applicationContext),
|
||||
WindGustsDataType(applicationContext),
|
||||
WindDirectionDataType(karooSystem, applicationContext),
|
||||
PrecipitationDataType(applicationContext),
|
||||
@ -8,6 +8,7 @@ import androidx.glance.appwidget.GlanceRemoteViews
|
||||
import de.timklge.karooheadwind.KarooHeadwindExtension
|
||||
import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
|
||||
import de.timklge.karooheadwind.getHeadingFlow
|
||||
import de.timklge.karooheadwind.getRelativeHeadingFlow
|
||||
import de.timklge.karooheadwind.screens.HeadwindSettings
|
||||
import de.timklge.karooheadwind.streamCurrentWeatherData
|
||||
import de.timklge.karooheadwind.streamDataFlow
|
||||
@ -34,44 +35,16 @@ import kotlin.math.cos
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@OptIn(ExperimentalGlanceRemoteViewsApi::class)
|
||||
class RelativeWindDirectionDataType(
|
||||
class HeadwindDirectionDataType(
|
||||
private val karooSystem: KarooSystemService,
|
||||
private val applicationContext: Context
|
||||
) : DataTypeImpl("karoo-headwind", "headwind") {
|
||||
private val glance = GlanceRemoteViews()
|
||||
|
||||
private fun signedAngleDifference(angle1: Double, angle2: Double): Double {
|
||||
val a1 = angle1 % 360
|
||||
val a2 = angle2 % 360
|
||||
var diff = abs(a1 - a2)
|
||||
|
||||
val sign = if (a1 < a2) {
|
||||
if (diff > 180.0) -1 else 1
|
||||
} else {
|
||||
if (diff > 180.0) 1 else -1
|
||||
}
|
||||
|
||||
if (diff > 180.0) {
|
||||
diff = 360.0 - diff
|
||||
}
|
||||
|
||||
return sign * diff
|
||||
}
|
||||
|
||||
override fun startStream(emitter: Emitter<StreamState>) {
|
||||
val job = CoroutineScope(Dispatchers.IO).launch {
|
||||
val currentWeatherData = applicationContext.streamCurrentWeatherData()
|
||||
|
||||
karooSystem
|
||||
.getHeadingFlow()
|
||||
.filter { it >= 0 }
|
||||
.combine(currentWeatherData) { cardinalDirection, data -> cardinalDirection to data }
|
||||
.collect { (cardinalDirection, data) ->
|
||||
val bearing = cardinalDirection * 45.0
|
||||
val windBearing = data.current.windDirection + 180
|
||||
|
||||
val diff = (signedAngleDifference(bearing, windBearing) + 360) % 360
|
||||
Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff")
|
||||
karooSystem.getRelativeHeadingFlow(applicationContext)
|
||||
.collect { diff ->
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to diff))))
|
||||
}
|
||||
}
|
||||
@ -107,7 +80,7 @@ class RelativeWindDirectionDataType(
|
||||
val windSpeedText = if(streamData.settings.showWindspeedOverlay) "${headwindSpeed.roundToInt()}" else null
|
||||
|
||||
val result = glance.compose(context, DpSize.Unspecified) {
|
||||
RelativeWindDirection(windDirection.roundToInt(), config.textSize, windSpeedText)
|
||||
HeadwindDirection(windDirection.roundToInt(), config.textSize, windSpeedText)
|
||||
}
|
||||
|
||||
emitter.updateView(result.remoteViews)
|
||||
@ -47,7 +47,7 @@ fun getArrowResourceByBearing(bearing: Int): Int {
|
||||
@OptIn(ExperimentalGlancePreviewApi::class)
|
||||
@Preview(widthDp = 200, heightDp = 150)
|
||||
@Composable
|
||||
fun RelativeWindDirection(bearing: Int, fontSize: Int, overlayText: String? = null) {
|
||||
fun HeadwindDirection(bearing: Int, fontSize: Int, overlayText: String? = null) {
|
||||
Box(
|
||||
modifier = GlanceModifier.fillMaxSize().padding(5.dp),
|
||||
contentAlignment = Alignment(
|
||||
@ -0,0 +1,45 @@
|
||||
package de.timklge.karooheadwind.datatypes
|
||||
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
|
||||
import de.timklge.karooheadwind.getRelativeHeadingFlow
|
||||
import de.timklge.karooheadwind.screens.HeadwindSettings
|
||||
import de.timklge.karooheadwind.streamCurrentWeatherData
|
||||
import de.timklge.karooheadwind.streamSettings
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.extension.DataTypeImpl
|
||||
import io.hammerhead.karooext.internal.Emitter
|
||||
import io.hammerhead.karooext.models.DataPoint
|
||||
import io.hammerhead.karooext.models.DataType
|
||||
import io.hammerhead.karooext.models.StreamState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.cos
|
||||
|
||||
class HeadwindSpeedDataType(
|
||||
private val karooSystem: KarooSystemService,
|
||||
private val context: Context) : DataTypeImpl("karoo-headwind", "headwindSpeed"){
|
||||
|
||||
data class StreamData(val value: Double, val data: OpenMeteoCurrentWeatherResponse, val settings: HeadwindSettings)
|
||||
|
||||
override fun startStream(emitter: Emitter<StreamState>) {
|
||||
val job = CoroutineScope(Dispatchers.IO).launch {
|
||||
karooSystem.getRelativeHeadingFlow(context)
|
||||
.combine(context.streamCurrentWeatherData()) { value, data -> value to data }
|
||||
.combine(context.streamSettings()) { (value, data), settings -> StreamData(value, data, settings) }
|
||||
.collect { streamData ->
|
||||
val windSpeed = streamData.data.current.windSpeed
|
||||
val windDirection = streamData.value
|
||||
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
|
||||
|
||||
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to headwindSpeed))))
|
||||
}
|
||||
}
|
||||
|
||||
emitter.setCancellable {
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,7 +49,7 @@ fun Weather(current: WeatherInterpretation, windBearing: Int, windSpeed: Int, wi
|
||||
modifier = GlanceModifier.height(imageH.dp).width(imageW.dp),
|
||||
provider = ImageProvider(getWeatherIcon(current)),
|
||||
contentDescription = "Current weather information",
|
||||
contentScale = ContentScale.FillBounds,
|
||||
contentScale = ContentScale.Fit,
|
||||
colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White))
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<resources>
|
||||
<string name="app_name">WindDir</string>
|
||||
<string name="app_name">Headwind</string>
|
||||
<string name="extension_name">headwind</string>
|
||||
<string name="headwind">Wind direction</string>
|
||||
<string name="headwind_description">Current wind direction relative to the riding direction</string>
|
||||
<string name="headwind">Headwind</string>
|
||||
<string name="headwind_description">Current headwind direction</string>
|
||||
<string name="relativeHumidity">Humidity</string>
|
||||
<string name="relativeHumidity_description">Relative humidity in percent</string>
|
||||
<string name="cloudCover">Cloud cover</string>
|
||||
@ -19,4 +19,6 @@
|
||||
<string name="surfacePressure_description">Atmospheric pressure at surface in configured unit</string>
|
||||
<string name="weather">Weather</string>
|
||||
<string name="weather_description">Current weather conditions</string>
|
||||
<string name="headwind_speed">Headwind speed</string>
|
||||
<string name="headwind_speed_description">Current headwind speed</string>
|
||||
</resources>
|
||||
@ -18,6 +18,13 @@
|
||||
icon="@drawable/ic_launcher"
|
||||
typeId="weather" />
|
||||
|
||||
<DataType
|
||||
description="@string/headwind_speed_description"
|
||||
displayName="@string/headwind_speed"
|
||||
graphical="false"
|
||||
icon="@drawable/ic_launcher"
|
||||
typeId="headwindSpeed" />
|
||||
|
||||
<DataType
|
||||
description="@string/relativeHumidity_description"
|
||||
displayName="@string/relativeHumidity"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user