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