Compare commits
5 Commits
master
...
1.5-beta-3
| Author | SHA1 | Date | |
|---|---|---|---|
| a8f7f53d66 | |||
| 823fe3a8bb | |||
| 522364dd84 | |||
| 855ce46b99 | |||
| 0332e032d4 |
34
.github/workflows/android.yml
vendored
34
.github/workflows/android.yml
vendored
@ -3,10 +3,10 @@ name: Build
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "**" ]
|
||||
tags: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "**" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -19,13 +19,14 @@ jobs:
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
- name: Set up environment variables
|
||||
run: |
|
||||
echo "GPR_USER=${{ github.actor }}" >> $GITHUB_ENV
|
||||
echo "GPR_KEY=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
|
||||
echo "GPR_USER=${{ secrets.GHUB_USER || github.actor }}" >> $GITHUB_ENV
|
||||
echo "GPR_KEY=${{ secrets.GHUB_TOKEN || secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
|
||||
echo "KEY_ALIAS=${{ secrets.KEY_ALIAS }}" >> $GITHUB_ENV
|
||||
echo "KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> $GITHUB_ENV
|
||||
echo "KEYSTORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> $GITHUB_ENV
|
||||
echo "KEYSTORE_BASE64=${{ secrets.KEYSTORE_BASE64 }}" >> $GITHUB_ENV
|
||||
echo "BUILD_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV
|
||||
echo "BASE_URL=${{ secrets.BASE_URL || 'https://github.com/timklge/karoo-headwind/releases/latest/download' }}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v4
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
@ -34,25 +35,30 @@ jobs:
|
||||
distribution: 'temurin'
|
||||
cache: gradle
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Archive APK
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app-release.apk
|
||||
path: app/build/outputs/apk/release/app-release.apk
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: ncipollo/release-action@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: ${{ github.ref_name }}
|
||||
prerelease: false
|
||||
generateReleaseNotes: true
|
||||
artifacts: app/build/outputs/apk/release/app-release.apk, app/manifest.json, app/karoo-headwind.png, preview0.png, preview1.png, preview2.png, preview3.png
|
||||
draft: false
|
||||
generate_release_notes: true
|
||||
make_latest: true
|
||||
files: |
|
||||
app/build/outputs/apk/release/app-release.apk
|
||||
app/manifest.json
|
||||
app/karoo-headwind.png
|
||||
preview0.png
|
||||
preview1.png
|
||||
preview2.png
|
||||
preview3.png
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,3 +9,4 @@
|
||||
.cxx
|
||||
local.properties
|
||||
/app/release
|
||||
app/manifest.json
|
||||
@ -1,3 +1,4 @@
|
||||
import com.android.build.gradle.tasks.ProcessApplicationManifest
|
||||
import java.util.Base64
|
||||
|
||||
plugins {
|
||||
@ -60,29 +61,38 @@ tasks.register("generateManifest") {
|
||||
group = "build"
|
||||
|
||||
doLast {
|
||||
val baseUrl = System.getenv("BASE_URL") ?: "https://github.com/timklge/karoo-headwind/releases/latest/download"
|
||||
val manifestFile = file("$projectDir/manifest.json")
|
||||
val manifest = mapOf(
|
||||
"label" to "Headwind",
|
||||
"packageName" to "de.timklge.karooheadwind",
|
||||
"iconUrl" to "https://github.com/timklge/karoo-headwind/releases/latest/download/karoo-headwind.png",
|
||||
"latestApkUrl" to "https://github.com/timklge/karoo-headwind/releases/latest/download/app-release.apk",
|
||||
"iconUrl" to "$baseUrl/karoo-headwind.png",
|
||||
"latestApkUrl" to "$baseUrl/app-release.apk",
|
||||
"latestVersion" to android.defaultConfig.versionName,
|
||||
"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 "* Remove crashlytics\n" +
|
||||
"releaseNotes" to "* Refactor unit conversions\n* Remove crashlytics\n" +
|
||||
"* Reduce refresh rate on K2, add refresh rate setting\n" +
|
||||
"screenshotUrls" to listOf(
|
||||
"https://github.com/timklge/karoo-headwind/releases/latest/download/preview1.png",
|
||||
"https://github.com/timklge/karoo-headwind/releases/latest/download/preview3.png",
|
||||
"https://github.com/timklge/karoo-headwind/releases/latest/download/preview2.png",
|
||||
"https://github.com/timklge/karoo-headwind/releases/latest/download/preview0.png",
|
||||
"$baseUrl/preview1.png",
|
||||
"$baseUrl/preview3.png",
|
||||
"$baseUrl/preview2.png",
|
||||
"$baseUrl/preview0.png",
|
||||
)
|
||||
)
|
||||
|
||||
val gson = groovy.json.JsonBuilder(manifest).toPrettyString()
|
||||
manifestFile.writeText(gson)
|
||||
println("Generated manifest.json with version ${android.defaultConfig.versionName} (${android.defaultConfig.versionCode})")
|
||||
|
||||
if (System.getenv()["BASE_URL"] != null){
|
||||
val androidManifestFile = file("$projectDir/src/main/AndroidManifest.xml")
|
||||
var androidManifestContent = androidManifestFile.readText()
|
||||
androidManifestContent = androidManifestContent.replace("\$BASE_URL\$", baseUrl)
|
||||
androidManifestFile.writeText(androidManifestContent)
|
||||
println("Replaced \$BASE_URL$ in AndroidManifest.xml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,6 +100,12 @@ tasks.named("assemble") {
|
||||
dependsOn("generateManifest")
|
||||
}
|
||||
|
||||
tasks.withType<ProcessApplicationManifest>().configureEach {
|
||||
if (name == "processDebugMainManifest" || name == "processReleaseMainManifest") {
|
||||
dependsOn(tasks.named("generateManifest"))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.mapbox.sdk.turf)
|
||||
implementation(libs.hammerhead.karoo.ext)
|
||||
|
||||
@ -32,6 +32,6 @@
|
||||
|
||||
<meta-data
|
||||
android:name="io.hammerhead.karooext.MANIFEST_URL"
|
||||
android:value="https://github.com/timklge/karoo-headwind/releases/latest/download/manifest.json" />
|
||||
android:value="$BASE_URL$/manifest.json" />
|
||||
</application>
|
||||
</manifest>
|
||||
@ -210,6 +210,22 @@ fun Context.streamCurrentForecastWeatherData(): Flow<WeatherDataResponse?> {
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
fun lerp(
|
||||
start: Double,
|
||||
end: Double,
|
||||
factor: Double
|
||||
): Double {
|
||||
return start + (end - start) * factor
|
||||
}
|
||||
|
||||
fun lerp(
|
||||
start: Int,
|
||||
end: Int,
|
||||
factor: Double
|
||||
): Int {
|
||||
return (start + (end - start) * factor).toInt()
|
||||
}
|
||||
|
||||
fun lerpNullable(
|
||||
start: Double?,
|
||||
end: Double?,
|
||||
@ -266,12 +282,12 @@ fun lerpWeather(
|
||||
return WeatherData(
|
||||
time = (start.time + (end.time - start.time) * factor).toLong(),
|
||||
temperature = start.temperature + (end.temperature - start.temperature) * factor,
|
||||
relativeHumidity = lerpNullable(start.relativeHumidity, end.relativeHumidity, factor),
|
||||
relativeHumidity = lerp(start.relativeHumidity, end.relativeHumidity, factor),
|
||||
precipitation = start.precipitation + (end.precipitation - start.precipitation) * factor,
|
||||
precipitationProbability = lerpNullable(start.precipitationProbability, end.precipitationProbability, factor),
|
||||
cloudCover = lerpNullable(start.cloudCover, end.cloudCover, factor),
|
||||
surfacePressure = lerpNullable(start.surfacePressure, end.surfacePressure, factor),
|
||||
sealevelPressure = lerpNullable(start.sealevelPressure, end.sealevelPressure, factor),
|
||||
cloudCover = lerp(start.cloudCover, end.cloudCover, factor),
|
||||
surfacePressure = lerp(start.surfacePressure, end.surfacePressure, factor),
|
||||
sealevelPressure = lerp(start.sealevelPressure, end.sealevelPressure, factor),
|
||||
windSpeed = start.windSpeed + (end.windSpeed - start.windSpeed) * factor,
|
||||
windDirection = lerpAngle(start.windDirection, end.windDirection, factor),
|
||||
windGusts = start.windGusts + (end.windGusts - start.windGusts) * factor,
|
||||
|
||||
@ -4,16 +4,23 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import de.timklge.karooheadwind.KarooHeadwindExtension
|
||||
import de.timklge.karooheadwind.streamCurrentWeatherData
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
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.StreamState
|
||||
import io.hammerhead.karooext.models.UpdateGraphicConfig
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
import io.hammerhead.karooext.models.ViewConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -22,20 +29,25 @@ abstract class BaseDataType(
|
||||
private val applicationContext: Context,
|
||||
dataTypeId: String
|
||||
) : DataTypeImpl("karoo-headwind", dataTypeId) {
|
||||
abstract fun getValue(data: WeatherData): Double?
|
||||
abstract fun getValue(data: WeatherData, userProfile: UserProfile): Double?
|
||||
|
||||
open fun getFormatDataType(): String? = null
|
||||
|
||||
override fun startStream(emitter: Emitter<StreamState>) {
|
||||
Log.d(KarooHeadwindExtension.TAG, "start $dataTypeId stream")
|
||||
val job = CoroutineScope(Dispatchers.IO).launch {
|
||||
val currentWeatherData = applicationContext.streamCurrentWeatherData(karooSystemService)
|
||||
data class StreamData(val weatherData: WeatherData, val userProfile: UserProfile)
|
||||
|
||||
val currentWeatherData = combine(applicationContext.streamCurrentWeatherData(karooSystemService).filterNotNull(), karooSystemService.streamUserProfile()) { weatherData, userProfile ->
|
||||
StreamData(weatherData, userProfile)
|
||||
}
|
||||
|
||||
val refreshRate = karooSystemService.getRefreshRateInMilliseconds(applicationContext)
|
||||
|
||||
currentWeatherData
|
||||
.filterNotNull()
|
||||
currentWeatherData.filterNotNull()
|
||||
.throttle(refreshRate)
|
||||
.collect { data ->
|
||||
val value = getValue(data)
|
||||
.collect { (data, userProfile) ->
|
||||
val value = getValue(data, userProfile)
|
||||
Log.d(KarooHeadwindExtension.TAG, "$dataTypeId: $value")
|
||||
|
||||
if (value != null) {
|
||||
@ -50,4 +62,12 @@ abstract class BaseDataType(
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) {
|
||||
Log.d(KarooHeadwindExtension.TAG, "Starting $dataTypeId view with $emitter")
|
||||
|
||||
if (getFormatDataType() != null){
|
||||
emitter.onNext(UpdateGraphicConfig(formatDataTypeId = getFormatDataType()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,9 +3,10 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class CloudCoverDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "cloudCover"){
|
||||
override fun getValue(data: WeatherData): Double? {
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? {
|
||||
return data.cloudCover
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,9 @@ import de.timklge.karooheadwind.streamUpcomingRoute
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.streamWidgetSettings
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.util.celciusInUserUnit
|
||||
import de.timklge.karooheadwind.util.millimetersInUserUnit
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherDataForLocation
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
|
||||
@ -116,7 +119,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
|
||||
WeatherData(
|
||||
time = forecastTime,
|
||||
temperature = forecastTemperature,
|
||||
relativeHumidity = 20.0,
|
||||
relativeHumidity = 20,
|
||||
precipitation = forecastPrecipitation,
|
||||
cloudCover = 3.0,
|
||||
sealevelPressure = 1013.25,
|
||||
@ -139,7 +142,7 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
|
||||
current = WeatherData(
|
||||
time = timeAtFullHour,
|
||||
temperature = 20.0,
|
||||
relativeHumidity = 20.0,
|
||||
relativeHumidity = 20,
|
||||
precipitation = 0.0,
|
||||
cloudCover = 3.0,
|
||||
sealevelPressure = 1013.25,
|
||||
@ -311,11 +314,11 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
|
||||
arrowBitmap = baseBitmap,
|
||||
current = interpretation,
|
||||
windBearing = data.current.windDirection.roundToInt(),
|
||||
windSpeed = data.current.windSpeed.roundToInt(),
|
||||
windGusts = data.current.windGusts.roundToInt(),
|
||||
precipitation = data.current.precipitation,
|
||||
windSpeed = msInUserUnit(data.current.windSpeed, settingsAndProfile.isImperial).roundToInt(),
|
||||
windGusts = msInUserUnit(data.current.windGusts, settingsAndProfile.isImperial).roundToInt(),
|
||||
precipitation = millimetersInUserUnit(data.current.precipitation, settingsAndProfile.isImperial),
|
||||
precipitationProbability = null,
|
||||
temperature = data.current.temperature.roundToInt(),
|
||||
temperature = celciusInUserUnit(data.current.temperature, settingsAndProfile.isImperialTemperature).roundToInt(),
|
||||
temperatureUnit = if (settingsAndProfile.isImperialTemperature) TemperatureUnit.FAHRENHEIT else TemperatureUnit.CELSIUS,
|
||||
timeLabel = formattedTime,
|
||||
dateLabel = if (hasNewDate) formattedDate else null,
|
||||
@ -337,11 +340,11 @@ abstract class ForecastDataType(private val karooSystem: KarooSystemService, typ
|
||||
arrowBitmap = baseBitmap,
|
||||
current = interpretation,
|
||||
windBearing = weatherData?.windDirection?.roundToInt() ?: 0,
|
||||
windSpeed = weatherData?.windSpeed?.roundToInt() ?: 0,
|
||||
windGusts = weatherData?.windGusts?.roundToInt() ?: 0,
|
||||
precipitation = weatherData?.precipitation ?: 0.0,
|
||||
windSpeed = msInUserUnit(weatherData?.windSpeed ?: 0.0, settingsAndProfile.isImperial).roundToInt(),
|
||||
windGusts = msInUserUnit(weatherData?.windGusts ?: 0.0, settingsAndProfile.isImperial).roundToInt(),
|
||||
precipitation = millimetersInUserUnit(weatherData?.precipitation ?: 0.0, settingsAndProfile.isImperial),
|
||||
precipitationProbability = weatherData?.precipitationProbability?.toInt(),
|
||||
temperature = weatherData?.temperature?.roundToInt() ?: 0,
|
||||
temperature = celciusInUserUnit(weatherData?.temperature ?: 0.0, settingsAndProfile.isImperialTemperature).roundToInt(),
|
||||
temperatureUnit = if (settingsAndProfile.isImperialTemperature) TemperatureUnit.FAHRENHEIT else TemperatureUnit.CELSIUS,
|
||||
timeLabel = formattedTime,
|
||||
dateLabel = if (hasNewDate) formattedDate else null,
|
||||
|
||||
@ -44,7 +44,6 @@ fun GraphicalForecast(
|
||||
provider = ImageProvider(getWeatherIcon(current, isNight)),
|
||||
contentDescription = "Current weather information",
|
||||
contentScale = ContentScale.Fit,
|
||||
colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,9 @@ import de.timklge.karooheadwind.getRelativeHeadingFlow
|
||||
import de.timklge.karooheadwind.streamCurrentWeatherData
|
||||
import de.timklge.karooheadwind.streamDatatypeIsVisible
|
||||
import de.timklge.karooheadwind.streamSettings
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.extension.DataTypeImpl
|
||||
import io.hammerhead.karooext.internal.Emitter
|
||||
@ -24,6 +26,7 @@ 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.UserProfile
|
||||
import io.hammerhead.karooext.models.ViewConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -63,7 +66,7 @@ class HeadwindDirectionDataType(
|
||||
val value = (streamData.headingResponse as? HeadingResponse.Value)?.diff
|
||||
|
||||
var returnValue = 0.0
|
||||
if (value == null || streamData.absoluteWindDirection == null || streamData.settings == null || streamData.windSpeed == null){
|
||||
if (value == null || streamData.absoluteWindDirection == null || streamData.windSpeed == null){
|
||||
var errorCode = 1.0
|
||||
var headingResponse = streamData.headingResponse
|
||||
|
||||
@ -71,7 +74,7 @@ class HeadwindDirectionDataType(
|
||||
headingResponse = HeadingResponse.NoWeatherData
|
||||
}
|
||||
|
||||
if (streamData.settings?.welcomeDialogAccepted == false){
|
||||
if (streamData.settings.welcomeDialogAccepted == false){
|
||||
errorCode = ERROR_APP_NOT_SET_UP.toDouble()
|
||||
} else if (headingResponse is HeadingResponse.NoGps){
|
||||
errorCode = ERROR_NO_GPS.toDouble()
|
||||
@ -106,7 +109,12 @@ class HeadwindDirectionDataType(
|
||||
}
|
||||
}
|
||||
|
||||
data class DirectionAndSpeed(val bearing: Double, val speed: Double?, val isVisible: Boolean)
|
||||
data class DirectionAndSpeed(
|
||||
val bearing: Double,
|
||||
val speed: Double?,
|
||||
val isVisible: Boolean,
|
||||
val isImperial: Boolean
|
||||
)
|
||||
|
||||
private fun previewFlow(): Flow<DirectionAndSpeed> {
|
||||
return flow {
|
||||
@ -114,7 +122,12 @@ class HeadwindDirectionDataType(
|
||||
val bearing = (0..360).random().toDouble()
|
||||
val windSpeed = (0..20).random()
|
||||
|
||||
emit(DirectionAndSpeed(bearing, windSpeed.toDouble(), true))
|
||||
emit(DirectionAndSpeed(
|
||||
bearing,
|
||||
windSpeed.toDouble(),
|
||||
true,
|
||||
true
|
||||
))
|
||||
|
||||
delay(2_000)
|
||||
}
|
||||
@ -144,8 +157,8 @@ class HeadwindDirectionDataType(
|
||||
emitAll(UserWindSpeedDataType.streamValues(context, karooSystem))
|
||||
}
|
||||
|
||||
combine(directionFlow, speedFlow, karooSystem.streamDatatypeIsVisible(dataTypeId)) { direction, speed, isVisible ->
|
||||
DirectionAndSpeed(direction, speed, isVisible)
|
||||
combine(directionFlow, speedFlow, karooSystem.streamDatatypeIsVisible(dataTypeId), karooSystem.streamUserProfile()) { direction, speed, isVisible, profile ->
|
||||
DirectionAndSpeed(direction, speed, isVisible, profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL)
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,14 +175,15 @@ class HeadwindDirectionDataType(
|
||||
}
|
||||
|
||||
val windDirection = streamData.bearing
|
||||
val windSpeed = streamData.speed
|
||||
val windSpeed = streamData.speed ?: 0.0
|
||||
val windSpeedUserUnit = msInUserUnit(windSpeed, streamData.isImperial)
|
||||
|
||||
val result = glance.compose(context, DpSize.Unspecified) {
|
||||
HeadwindDirection(
|
||||
baseBitmap,
|
||||
windDirection.roundToInt(),
|
||||
config.textSize,
|
||||
windSpeed?.toInt()?.toString() ?: "",
|
||||
windSpeedUserUnit.roundToInt().toString(),
|
||||
preview = config.preview,
|
||||
wideMode = false
|
||||
)
|
||||
|
||||
@ -11,9 +11,12 @@ 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.StreamState
|
||||
import io.hammerhead.karooext.models.UpdateGraphicConfig
|
||||
import io.hammerhead.karooext.models.ViewConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@ -49,5 +52,9 @@ class HeadwindSpeedDataType(
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
override fun startView(context: Context, config: ViewConfig, emitter: ViewEmitter) {
|
||||
emitter.onNext(UpdateGraphicConfig(formatDataTypeId = DataType.Type.SPEED))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package de.timklge.karooheadwind.datatypes
|
||||
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.util.millimetersInUserUnit
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class PrecipitationDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "precipitation"){
|
||||
override fun getValue(data: WeatherData): Double {
|
||||
return data.precipitation
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double {
|
||||
return millimetersInUserUnit(data.precipitation, userProfile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL)
|
||||
}
|
||||
}
|
||||
@ -129,29 +129,8 @@ class RelativeGradeDataType(private val karooSystemService: KarooSystemService,
|
||||
|
||||
val refreshRate = karooSystemService.getRefreshRateInMilliseconds(context)
|
||||
|
||||
val windSpeedFlow = combine(context.streamSettings(karooSystemService), karooSystemService.streamUserProfile(), context.streamCurrentWeatherData(karooSystemService).filterNotNull()) { settings, profile, weatherData ->
|
||||
val isOpenMeteo = settings.weatherProvider == WeatherDataProvider.OPEN_METEO
|
||||
val profileIsImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL
|
||||
|
||||
if (isOpenMeteo) {
|
||||
if (profileIsImperial) { // OpenMeteo returns wind speed in mph
|
||||
val windSpeedInMilesPerHour = weatherData.windSpeed
|
||||
|
||||
windSpeedInMilesPerHour * 0.44704
|
||||
} else { // Wind speed reported by openmeteo is in km/h
|
||||
val windSpeedInKmh = weatherData.windSpeed
|
||||
|
||||
windSpeedInKmh * 0.277778
|
||||
}
|
||||
} else {
|
||||
if (profileIsImperial) { // OpenWeatherMap returns wind speed in mph
|
||||
val windSpeedInMilesPerHour = weatherData.windSpeed
|
||||
|
||||
windSpeedInMilesPerHour * 0.44704
|
||||
} else { // Wind speed reported by openweathermap is in m/s
|
||||
weatherData.windSpeed
|
||||
}
|
||||
}
|
||||
val windSpeedFlow = context.streamCurrentWeatherData(karooSystemService).filterNotNull().map { weatherData ->
|
||||
weatherData.windSpeed
|
||||
}
|
||||
|
||||
data class StreamValues(
|
||||
|
||||
@ -3,9 +3,10 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class RelativeHumidityDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "relativeHumidity"){
|
||||
override fun getValue(data: WeatherData): Double? {
|
||||
return data.relativeHumidity
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? {
|
||||
return data.relativeHumidity.toDouble()
|
||||
}
|
||||
}
|
||||
@ -3,9 +3,10 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class SealevelPressureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "sealevelPressure"){
|
||||
override fun getValue(data: WeatherData): Double? {
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? {
|
||||
return data.sealevelPressure
|
||||
}
|
||||
}
|
||||
@ -3,9 +3,10 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class SurfacePressureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "surfacePressure"){
|
||||
override fun getValue(data: WeatherData): Double? {
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double? {
|
||||
return data.surfacePressure
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@ import de.timklge.karooheadwind.streamDatatypeIsVisible
|
||||
import de.timklge.karooheadwind.streamSettings
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.extension.DataTypeImpl
|
||||
@ -133,11 +134,7 @@ class TailwindAndRideSpeedDataType(
|
||||
val absoluteWindDirection = weatherData?.windDirection
|
||||
val windSpeed = weatherData?.windSpeed
|
||||
val gustSpeed = weatherData?.windGusts
|
||||
val rideSpeed = if (isImperial){
|
||||
rideSpeedInMs * 2.23694
|
||||
} else {
|
||||
rideSpeedInMs * 3.6
|
||||
}
|
||||
val rideSpeed = rideSpeedInMs
|
||||
|
||||
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed, isVisible = isVisible)
|
||||
}
|
||||
@ -169,15 +166,21 @@ class TailwindAndRideSpeedDataType(
|
||||
WindDirectionIndicatorSetting.WIND_DIRECTION -> streamData.absoluteWindDirection + 180
|
||||
}
|
||||
|
||||
val text = streamData.rideSpeed?.let { String.format(Locale.current.platformLocale, "%.1f", it) } ?: ""
|
||||
val rideSpeedInUserUnit = msInUserUnit(streamData.rideSpeed ?: 0.0, streamData.isImperial)
|
||||
val text = String.format(Locale.current.platformLocale, "%.1f", rideSpeedInUserUnit)
|
||||
|
||||
val wideMode = config.gridSize.first == 60
|
||||
|
||||
val gustSpeedInUserUnit = msInUserUnit(streamData.gustSpeed ?: 0.0, streamData.isImperial)
|
||||
|
||||
val gustSpeedAddon = if (wideMode) {
|
||||
"-${streamData.gustSpeed?.roundToInt() ?: 0}"
|
||||
"-${gustSpeedInUserUnit.roundToInt()}"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
val windSpeedUserUnit = msInUserUnit(windSpeed, streamData.isImperial)
|
||||
|
||||
val subtextWithSign = when (streamData.settings.windDirectionIndicatorTextSetting) {
|
||||
WindDirectionIndicatorTextSetting.HEADWIND_SPEED -> {
|
||||
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
|
||||
@ -186,9 +189,12 @@ class TailwindAndRideSpeedDataType(
|
||||
val sign = if (headwindSpeed < 0) "+" else {
|
||||
if (headwindSpeed > 0) "-" else ""
|
||||
}
|
||||
"$sign${headwindSpeed.roundToInt().absoluteValue} ${windSpeed.roundToInt()}${gustSpeedAddon}"
|
||||
|
||||
val headwindSpeedUserUnit = msInUserUnit(headwindSpeed, streamData.isImperial)
|
||||
|
||||
"$sign${headwindSpeedUserUnit.roundToInt().absoluteValue} ${windSpeedUserUnit.roundToInt()}${gustSpeedAddon}"
|
||||
}
|
||||
WindDirectionIndicatorTextSetting.WIND_SPEED -> "${windSpeed.roundToInt()}${gustSpeedAddon}"
|
||||
WindDirectionIndicatorTextSetting.WIND_SPEED -> "${windSpeedUserUnit.roundToInt()}${gustSpeedAddon}"
|
||||
WindDirectionIndicatorTextSetting.NONE -> ""
|
||||
}
|
||||
|
||||
@ -197,11 +203,7 @@ class TailwindAndRideSpeedDataType(
|
||||
|
||||
if (streamData.settings.windDirectionIndicatorSetting == WindDirectionIndicatorSetting.HEADWIND_DIRECTION) {
|
||||
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
|
||||
val windSpeedInKmh = if (streamData.isImperial == true){
|
||||
headwindSpeed / 2.23694 * 3.6
|
||||
} else {
|
||||
headwindSpeed
|
||||
}
|
||||
val windSpeedInKmh = headwindSpeed * 3.6
|
||||
dayColor = interpolateWindColor(windSpeedInKmh, false, context)
|
||||
nightColor = interpolateWindColor(windSpeedInKmh, true, context)
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import de.timklge.karooheadwind.streamDatatypeIsVisible
|
||||
import de.timklge.karooheadwind.streamSettings
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.extension.DataTypeImpl
|
||||
@ -120,13 +121,8 @@ class TailwindDataType(
|
||||
val absoluteWindDirection = weatherData?.windDirection
|
||||
val windSpeed = weatherData?.windSpeed
|
||||
val gustSpeed = weatherData?.windGusts
|
||||
val rideSpeed = if (isImperial){
|
||||
rideSpeedInMs * 2.23694
|
||||
} else {
|
||||
rideSpeedInMs * 3.6
|
||||
}
|
||||
|
||||
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeed, isImperial = isImperial, gustSpeed = gustSpeed, isVisible = isVisible)
|
||||
StreamData(headingResponse, absoluteWindDirection, windSpeed, settings, rideSpeed = rideSpeedInMs, isImperial = isImperial, gustSpeed = gustSpeed, isVisible = isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,24 +160,26 @@ class TailwindDataType(
|
||||
val sign = if (headwindSpeed < 0) "+" else {
|
||||
if (headwindSpeed > 0) "-" else ""
|
||||
}
|
||||
"$sign${headwindSpeed.roundToInt().absoluteValue}"
|
||||
|
||||
val headwindSpeedUserUnit = msInUserUnit(headwindSpeed, streamData.isImperial)
|
||||
|
||||
"$sign${headwindSpeedUserUnit.roundToInt().absoluteValue}"
|
||||
}
|
||||
WindDirectionIndicatorTextSetting.WIND_SPEED -> windSpeed.roundToInt().toString()
|
||||
WindDirectionIndicatorTextSetting.WIND_SPEED -> msInUserUnit(windSpeed, streamData.isImperial).roundToInt().toString()
|
||||
WindDirectionIndicatorTextSetting.NONE -> ""
|
||||
}
|
||||
|
||||
val subtext = "${windSpeed.roundToInt()}-${streamData.gustSpeed?.roundToInt()}"
|
||||
val windSpeedUserUnit = msInUserUnit(windSpeed, streamData.isImperial)
|
||||
val gustSpeedUserUnit = msInUserUnit(streamData.gustSpeed ?: 0.0, streamData.isImperial)
|
||||
|
||||
val subtext = "${windSpeedUserUnit.roundToInt()}-${gustSpeedUserUnit.roundToInt()}"
|
||||
|
||||
var dayColor = Color(ContextCompat.getColor(context, R.color.black))
|
||||
var nightColor = Color(ContextCompat.getColor(context, R.color.white))
|
||||
|
||||
if (streamData.settings.windDirectionIndicatorSetting == WindDirectionIndicatorSetting.HEADWIND_DIRECTION) {
|
||||
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
|
||||
val windSpeedInKmh = if (streamData.isImperial){
|
||||
headwindSpeed / 2.23694 * 3.6
|
||||
} else {
|
||||
headwindSpeed
|
||||
}
|
||||
val windSpeedInKmh = headwindSpeed * 3.6
|
||||
dayColor = interpolateWindColor(windSpeedInKmh, false, context)
|
||||
nightColor = interpolateWindColor(windSpeedInKmh, true, context)
|
||||
}
|
||||
|
||||
@ -3,9 +3,15 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.DataType
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class TemperatureDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "temperature"){
|
||||
override fun getValue(data: WeatherData): Double {
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double {
|
||||
return data.temperature
|
||||
}
|
||||
|
||||
override fun getFormatDataType(): String? {
|
||||
return DataType.Type.TEMPERATURE
|
||||
}
|
||||
}
|
||||
@ -24,6 +24,9 @@ import de.timklge.karooheadwind.streamDatatypeIsVisible
|
||||
import de.timklge.karooheadwind.streamSettings
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.throttle
|
||||
import de.timklge.karooheadwind.util.celciusInUserUnit
|
||||
import de.timklge.karooheadwind.util.millimetersInUserUnit
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherInterpretation
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
@ -85,7 +88,7 @@ class WeatherDataType(
|
||||
emit(StreamData(
|
||||
WeatherData(
|
||||
Instant.now().epochSecond, 0.0,
|
||||
20.0, 50.0, 3.0, 0.0, 1013.25, 980.0, 15.0, 30.0, 30.0,
|
||||
20, 50.0, 3.0, 0.0, 1013.25, 980.0, 15.0, 30.0, 30.0,
|
||||
WeatherInterpretation.getKnownWeatherCodes().random(), isForecast = false,
|
||||
isNight = listOf(true, false).random()
|
||||
), HeadwindSettings(), isVisible = true))
|
||||
@ -147,11 +150,11 @@ class WeatherDataType(
|
||||
baseBitmap,
|
||||
current = interpretation,
|
||||
windBearing = data.windDirection.roundToInt(),
|
||||
windSpeed = data.windSpeed.roundToInt(),
|
||||
windGusts = data.windGusts.roundToInt(),
|
||||
precipitation = data.precipitation,
|
||||
windSpeed = msInUserUnit(data.windSpeed, userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
windGusts = msInUserUnit(data.windGusts, userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
precipitation = millimetersInUserUnit(data.precipitation, userProfile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL),
|
||||
precipitationProbability = null,
|
||||
temperature = data.temperature.roundToInt(),
|
||||
temperature = celciusInUserUnit(data.temperature, userProfile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
temperatureUnit = if (userProfile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
|
||||
timeLabel = formattedTime,
|
||||
rowAlignment = when (config.alignment){
|
||||
|
||||
@ -26,6 +26,7 @@ import io.hammerhead.karooext.internal.ViewEmitter
|
||||
import io.hammerhead.karooext.models.ShowCustomStreamState
|
||||
import io.hammerhead.karooext.models.StreamState
|
||||
import io.hammerhead.karooext.models.UpdateGraphicConfig
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
import io.hammerhead.karooext.models.ViewConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -51,7 +52,7 @@ class WindDirectionDataType(val karooSystem: KarooSystemService, context: Contex
|
||||
)
|
||||
}
|
||||
|
||||
override fun getValue(data: WeatherData): Double {
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double {
|
||||
return data.windDirection
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class WindGustsDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "windGusts"){
|
||||
override fun getValue(data: WeatherData): Double {
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double {
|
||||
return data.windGusts
|
||||
}
|
||||
}
|
||||
@ -3,9 +3,10 @@ package de.timklge.karooheadwind.datatypes
|
||||
import android.content.Context
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherData
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
|
||||
class WindSpeedDataType(karooSystemService: KarooSystemService, context: Context) : BaseDataType(karooSystemService, context, "windSpeed"){
|
||||
override fun getValue(data: WeatherData): Double {
|
||||
override fun getValue(data: WeatherData, userProfile: UserProfile): Double {
|
||||
return data.windSpeed
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,9 @@ import de.timklge.karooheadwind.streamCurrentWeatherData
|
||||
import de.timklge.karooheadwind.streamStats
|
||||
import de.timklge.karooheadwind.streamUpcomingRoute
|
||||
import de.timklge.karooheadwind.streamUserProfile
|
||||
import de.timklge.karooheadwind.util.celciusInUserUnit
|
||||
import de.timklge.karooheadwind.util.millimetersInUserUnit
|
||||
import de.timklge.karooheadwind.util.msInUserUnit
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.UserProfile
|
||||
import java.time.Instant
|
||||
@ -110,10 +113,10 @@ fun WeatherScreen(onFinish: () -> Unit) {
|
||||
baseBitmap = baseBitmap,
|
||||
current = WeatherInterpretation.fromWeatherCode(currentWeatherData?.weatherCode),
|
||||
windBearing = currentWeatherData?.windDirection?.roundToInt() ?: 0,
|
||||
windSpeed = currentWeatherData?.windSpeed?.roundToInt() ?: 0,
|
||||
windGusts = currentWeatherData?.windGusts?.roundToInt() ?: 0,
|
||||
precipitation = currentWeatherData?.precipitation ?: 0.0,
|
||||
temperature = currentWeatherData?.temperature?.toInt() ?: 0,
|
||||
windSpeed = msInUserUnit(currentWeatherData?.windSpeed ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
windGusts = msInUserUnit(currentWeatherData?.windGusts ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
precipitation = millimetersInUserUnit(currentWeatherData?.precipitation ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL),
|
||||
temperature = celciusInUserUnit(currentWeatherData?.temperature ?: 0.0, profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
temperatureUnit = if(profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.METRIC) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
|
||||
timeLabel = formattedTime,
|
||||
dateLabel = formattedDate,
|
||||
@ -230,10 +233,10 @@ fun WeatherScreen(onFinish: () -> Unit) {
|
||||
baseBitmap,
|
||||
current = interpretation,
|
||||
windBearing = weatherData?.windDirection?.roundToInt() ?: 0,
|
||||
windSpeed = weatherData?.windSpeed?.roundToInt() ?: 0,
|
||||
windGusts = weatherData?.windGusts?.roundToInt() ?: 0,
|
||||
precipitation = weatherData?.precipitation ?: 0.0,
|
||||
temperature = weatherData?.temperature?.toInt() ?: 0,
|
||||
windSpeed = msInUserUnit(currentWeatherData?.windSpeed ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
windGusts = msInUserUnit(currentWeatherData?.windGusts ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
precipitation = millimetersInUserUnit(weatherData?.precipitation ?: 0.0, profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL),
|
||||
temperature = celciusInUserUnit(weatherData?.temperature ?: 0.0, profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL).roundToInt(),
|
||||
temperatureUnit = if (profile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT,
|
||||
timeLabel = formattedForecastTime,
|
||||
dateLabel = formattedForecastDate,
|
||||
|
||||
@ -50,7 +50,7 @@ fun WeatherWidget(
|
||||
isImperial: Boolean,
|
||||
isNight: Boolean
|
||||
) {
|
||||
val fontSize = 20.sp
|
||||
val fontSize = 18.sp
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(5.dp),
|
||||
@ -106,7 +106,6 @@ fun WeatherWidget(
|
||||
)
|
||||
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
// Temperature (larger)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package de.timklge.karooheadwind.util
|
||||
|
||||
fun celciusInUserUnit(celcius: Double, isImperial: Boolean): Double {
|
||||
return if (isImperial) {
|
||||
celcius * 9.0 / 5 + 32.0
|
||||
} else {
|
||||
celcius
|
||||
}
|
||||
}
|
||||
|
||||
fun millimetersInUserUnit(millimeters: Double, isImperial: Boolean): Double {
|
||||
return if (isImperial) {
|
||||
millimeters / 25.4
|
||||
} else {
|
||||
millimeters
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the given speed value (m / s) in user unit (km/h or mph)
|
||||
fun msInUserUnit(ms: Double, isImperial: Boolean): Double {
|
||||
return if (isImperial) {
|
||||
ms * 2.2369362920544
|
||||
} else {
|
||||
ms * 3.6
|
||||
}
|
||||
}
|
||||
@ -6,12 +6,12 @@ import kotlinx.serialization.Serializable
|
||||
data class WeatherData(
|
||||
val time: Long,
|
||||
val temperature: Double,
|
||||
val relativeHumidity: Double? = null,
|
||||
val relativeHumidity: Int,
|
||||
val precipitation: Double,
|
||||
val precipitationProbability: Double? = null,
|
||||
val cloudCover: Double? = null,
|
||||
val sealevelPressure: Double? = null,
|
||||
val surfacePressure: Double? = null,
|
||||
val cloudCover: Double,
|
||||
val sealevelPressure: Double,
|
||||
val surfacePressure: Double,
|
||||
val windSpeed: Double,
|
||||
val windDirection: Double,
|
||||
val windGusts: Double,
|
||||
|
||||
@ -12,7 +12,7 @@ data class OpenMeteoWeatherData(
|
||||
@SerialName("precipitation") val precipitation: Double,
|
||||
@SerialName("cloud_cover") val cloudCover: Int,
|
||||
@SerialName("surface_pressure") val surfacePressure: Double,
|
||||
@SerialName("pressure_msl") val sealevelPressure: Double? = null,
|
||||
@SerialName("pressure_msl") val sealevelPressure: Double,
|
||||
@SerialName("wind_speed_10m") val windSpeed: Double,
|
||||
@SerialName("wind_direction_10m") val windDirection: Double,
|
||||
@SerialName("wind_gusts_10m") val windGusts: Double,
|
||||
@ -21,7 +21,7 @@ data class OpenMeteoWeatherData(
|
||||
) {
|
||||
fun toWeatherData(): WeatherData = WeatherData(
|
||||
temperature = temperature,
|
||||
relativeHumidity = relativeHumidity.toDouble(),
|
||||
relativeHumidity = relativeHumidity,
|
||||
precipitation = precipitation,
|
||||
cloudCover = cloudCover.toDouble(),
|
||||
surfacePressure = surfacePressure,
|
||||
@ -32,7 +32,7 @@ data class OpenMeteoWeatherData(
|
||||
weatherCode = weatherCode,
|
||||
time = time,
|
||||
isForecast = false,
|
||||
isNight = isDay == 0
|
||||
isNight = isDay == 0,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,11 @@ data class OpenMeteoWeatherForecastData(
|
||||
@SerialName("wind_speed_10m") val windSpeed: List<Double>,
|
||||
@SerialName("wind_direction_10m") val windDirection: List<Double>,
|
||||
@SerialName("wind_gusts_10m") val windGusts: List<Double>,
|
||||
@SerialName("cloud_cover") val cloudCover: List<Double>,
|
||||
@SerialName("surface_pressure") val surfacePressure: List<Double>,
|
||||
@SerialName("pressure_msl") val sealevelPressure: List<Double>,
|
||||
@SerialName("is_day") val isDay: List<Int>,
|
||||
@SerialName("relative_humidity_2m") val relativeHumidity: List<Int>,
|
||||
) {
|
||||
fun toWeatherData(): List<WeatherData> {
|
||||
return time.mapIndexed { index, t ->
|
||||
@ -29,6 +33,10 @@ data class OpenMeteoWeatherForecastData(
|
||||
isNight = isDay[index] == 0,
|
||||
time = t,
|
||||
isForecast = true,
|
||||
cloudCover = cloudCover[index],
|
||||
surfacePressure = surfacePressure[index],
|
||||
sealevelPressure = sealevelPressure[index],
|
||||
relativeHumidity = relativeHumidity[index],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,7 @@ package de.timklge.karooheadwind.weatherprovider.openmeteo
|
||||
import android.util.Log
|
||||
import de.timklge.karooheadwind.HeadwindSettings
|
||||
import de.timklge.karooheadwind.KarooHeadwindExtension
|
||||
import de.timklge.karooheadwind.PrecipitationUnit
|
||||
import de.timklge.karooheadwind.TemperatureUnit
|
||||
import de.timklge.karooheadwind.WeatherDataProvider
|
||||
import de.timklge.karooheadwind.WindUnit
|
||||
import de.timklge.karooheadwind.datatypes.GpsCoordinates
|
||||
import de.timklge.karooheadwind.jsonWithUnknownKeys
|
||||
import de.timklge.karooheadwind.weatherprovider.WeatherDataResponse
|
||||
@ -28,16 +25,12 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class OpenMeteoWeatherProvider : WeatherProvider {
|
||||
@OptIn(FlowPreview::class)
|
||||
private suspend fun makeOpenMeteoWeatherRequest(karooSystemService: KarooSystemService, gpsCoordinates: List<GpsCoordinates>, settings: HeadwindSettings, profile: UserProfile?): HttpResponseState.Complete {
|
||||
val precipitationUnit = if (profile?.preferredUnit?.distance != UserProfile.PreferredUnit.UnitType.IMPERIAL) PrecipitationUnit.MILLIMETERS else PrecipitationUnit.INCH
|
||||
val temperatureUnit = if (profile?.preferredUnit?.temperature != UserProfile.PreferredUnit.UnitType.IMPERIAL) TemperatureUnit.CELSIUS else TemperatureUnit.FAHRENHEIT
|
||||
val windUnit = if (profile?.preferredUnit?.distance != UserProfile.PreferredUnit.UnitType.IMPERIAL) WindUnit.KILOMETERS_PER_HOUR else WindUnit.MILES_PER_HOUR
|
||||
|
||||
private suspend fun makeOpenMeteoWeatherRequest(karooSystemService: KarooSystemService, gpsCoordinates: List<GpsCoordinates>): HttpResponseState.Complete {
|
||||
val response = callbackFlow {
|
||||
// https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t=is_day,surface_pressure,pressure_msl,temperature_2m,relative_humidity_2m,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,precipitation_probability,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m&timeformat=unixtime&past_hours=1&forecast_days=1&forecast_hours=12
|
||||
val lats = gpsCoordinates.joinToString(",") { String.format(Locale.US, "%.6f", it.lat) }
|
||||
val lons = gpsCoordinates.joinToString(",") { String.format(Locale.US, "%.6f", it.lon) }
|
||||
val url = "https://api.open-meteo.com/v1/forecast?latitude=${lats}&longitude=${lons}¤t=is_day,surface_pressure,pressure_msl,temperature_2m,relative_humidity_2m,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,precipitation_probability,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m,is_day&timeformat=unixtime&past_hours=0&forecast_days=1&forecast_hours=12&wind_speed_unit=${windUnit.id}&precipitation_unit=${precipitationUnit.id}&temperature_unit=${temperatureUnit.id}"
|
||||
val url = "https://api.open-meteo.com/v1/forecast?latitude=${lats}&longitude=${lons}¤t=is_day,surface_pressure,pressure_msl,temperature_2m,relative_humidity_2m,precipitation,weather_code,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,precipitation_probability,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m,is_day,surface_pressure,pressure_msl,relative_humidity_2m,cloud_cover&timeformat=unixtime&past_hours=0&forecast_days=1&forecast_hours=12&wind_speed_unit=ms"
|
||||
|
||||
Log.d(KarooHeadwindExtension.TAG, "Http request to ${url}...")
|
||||
|
||||
@ -84,7 +77,7 @@ class OpenMeteoWeatherProvider : WeatherProvider {
|
||||
settings: HeadwindSettings,
|
||||
profile: UserProfile?
|
||||
): WeatherDataResponse {
|
||||
val openMeteoResponse = makeOpenMeteoWeatherRequest(karooSystem, coordinates, settings, profile)
|
||||
val openMeteoResponse = makeOpenMeteoWeatherRequest(karooSystem, coordinates)
|
||||
val responseBody = openMeteoResponse.body?.let { String(it) } ?: throw WeatherProviderException(500, "Null response from OpenMeteo")
|
||||
|
||||
val weatherData = if (coordinates.size == 1) {
|
||||
@ -102,4 +95,4 @@ class OpenMeteoWeatherProvider : WeatherProvider {
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ data class OpenWeatherMapForecastData(
|
||||
|
||||
return WeatherData(
|
||||
temperature = temp,
|
||||
relativeHumidity = humidity.toDouble(),
|
||||
relativeHumidity = humidity,
|
||||
precipitation = rain?.h1 ?: 0.0,
|
||||
cloudCover = clouds.toDouble(),
|
||||
surfacePressure = pressure.toDouble(),
|
||||
|
||||
@ -23,7 +23,7 @@ data class OpenWeatherMapWeatherData(
|
||||
|
||||
fun toWeatherData(): WeatherData = WeatherData(
|
||||
temperature = temp,
|
||||
relativeHumidity = humidity.toDouble(),
|
||||
relativeHumidity = humidity,
|
||||
precipitation = rain?.h1 ?: 0.0,
|
||||
cloudCover = clouds.toDouble(),
|
||||
surfacePressure = pressure.toDouble(),
|
||||
|
||||
@ -69,7 +69,7 @@ class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvide
|
||||
profile: UserProfile?
|
||||
): WeatherDataResponse {
|
||||
|
||||
val response = makeOpenWeatherMapRequest(karooSystem, coordinates, apiKey, profile)
|
||||
val response = makeOpenWeatherMapRequest(karooSystem, coordinates, apiKey)
|
||||
val responseBody = response.body?.let { String(it) } ?: throw Exception("Null response from OpenWeatherMap")
|
||||
|
||||
val responses = mutableListOf<WeatherDataForLocation>()
|
||||
@ -89,21 +89,15 @@ class OpenWeatherMapWeatherProvider(private val apiKey: String) : WeatherProvide
|
||||
private suspend fun makeOpenWeatherMapRequest(
|
||||
service: KarooSystemService,
|
||||
coordinates: List<GpsCoordinates>,
|
||||
apiKey: String,
|
||||
profile: UserProfile?
|
||||
apiKey: String
|
||||
): HttpResponseState.Complete {
|
||||
val response = callbackFlow {
|
||||
// OpenWeatherMap only supports setting imperial or metric units for all measurements, not individually for distance / temperature
|
||||
val unitsString = if (profile?.preferredUnit?.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL || profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL) {
|
||||
"imperial"
|
||||
} else {
|
||||
"metric"
|
||||
}
|
||||
val coordinate = coordinates.first()
|
||||
|
||||
// URL API 3.0 with onecall endpoint
|
||||
val url = "https://api.openweathermap.org/data/3.0/onecall?lat=${coordinate.lat}&lon=${coordinate.lon}" +
|
||||
"&appid=$apiKey&exclude=minutely,daily,alerts&units=${unitsString}"
|
||||
"&appid=$apiKey&exclude=minutely,daily,alerts&units=metric"
|
||||
|
||||
Log.d(KarooHeadwindExtension.TAG, "Http request to OpenWeatherMap API 3.0: $url")
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user