Add Bright Sky (Deutscher Wetterdienst) weather provider

This commit is contained in:
MM20 2021-09-22 18:22:54 +02:00
parent b8b936f182
commit cb44e24ae1
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
7 changed files with 181 additions and 1 deletions

View File

@ -16,6 +16,7 @@ import de.mm20.launcher2.preferences.WeatherProviders
import de.mm20.launcher2.weather.WeatherLocation
import de.mm20.launcher2.weather.WeatherProvider
import de.mm20.launcher2.weather.WeatherViewModel
import de.mm20.launcher2.weather.brightsky.BrightskyProvider
import de.mm20.launcher2.weather.here.HereProvider
import de.mm20.launcher2.weather.metno.MetNoProvider
import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider
@ -58,6 +59,9 @@ class PreferencesWeatherFragment : PreferenceFragmentCompat() {
MetNoProvider(context).takeIf { it.isAvailable() }?.let {
providers.add(WeatherProviders.MET_NO to it.name)
}
BrightskyProvider(context).takeIf { it.isAvailable() }?.let {
providers.add(WeatherProviders.BRIGHT_SKY to it.name)
}
if (providers.isEmpty()) {
providerPref.summary = context.getString(

View File

@ -154,6 +154,7 @@
<string name="websearch_dialog_url_error">In dieser URL fehlt der Platzhalter „${1}“</string>
<string name="preference_weather_provider">Wetterdienst</string>
<string name="provider_openweathermap">OpenWeatherMap</string>
<string name="provider_brightsky">Deutscher Wetterdienst (nur Deutschland)</string>
<string name="preference_automatic_location_disabled_summary">Von diesem Wetterdienst nicht unterstützt</string>
<string name="date_format_long_without_year">EE, d. MMMM</string>
<string name="weather_wind_direction_speed">%1$s • %2$s</string>
@ -253,6 +254,8 @@
<string name="weather_cloudy">Bedeckt</string>
<string name="weather_lightrainandthunder">Leichter Regen und Gewitter</string>
<string name="weather_snow">Schneefall</string>
<string name="weather_hail">Hagel</string>
<string name="weather_thunder">Gewitter</string>
<string name="weather_heavysnowshowers">Starke Schneeschauer</string>
<string name="weather_heavyrainshowers">Starke Regenschauer</string>
<string name="weather_rainshowersandthunder">Regenschauer und Gewitter</string>

View File

@ -193,6 +193,7 @@
<string name="preference_weather_provider">Provider</string>
<string name="provider_metno">MET Norway</string>
<string name="provider_openweathermap">OpenWeatherMap</string>
<string name="provider_brightsky">Deutscher Wetterdienst (Germany only)</string>
<string name="provider_here">HERE</string>
<string name="preference_automatic_location_disabled_summary">Not supported by this provider</string>
<string name="date_format_long_without_year">EE, MMMM d</string>
@ -406,6 +407,8 @@
<string name="weather_cloudy">Cloudy</string>
<string name="weather_lightrainandthunder">Light rain and thunder</string>
<string name="weather_snow">Snow</string>
<string name="weather_hail">Hail</string>
<string name="weather_thunder">Thunderstorm</string>
<string name="weather_heavysnowshowers">Heavy snow showers</string>
<string name="weather_heavyrainshowers">Heavy rain showers</string>
<string name="weather_rainshowersandthunder">Rain showers and thunder</string>

View File

@ -159,7 +159,8 @@ enum class IconShape(override val value: String) : PreferenceEnum {
enum class WeatherProviders(override val value: String) : PreferenceEnum {
OPENWEATHERMAP("0"),
HERE("3"),
MET_NO("2");
MET_NO("2"),
BRIGHT_SKY("4");
companion object {
fun byValue(value: String): WeatherProviders {

View File

@ -9,6 +9,7 @@ import androidx.core.content.getSystemService
import de.mm20.launcher2.ktx.checkPermission
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.preferences.WeatherProviders
import de.mm20.launcher2.weather.brightsky.BrightskyProvider
import de.mm20.launcher2.weather.here.HereProvider
import de.mm20.launcher2.weather.metno.MetNoProvider
import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider
@ -103,6 +104,7 @@ abstract class WeatherProvider<T : WeatherLocation> {
return when (LauncherPreferences.instance.weatherProvider) {
WeatherProviders.OPENWEATHERMAP -> OpenWeatherMapProvider(context)
WeatherProviders.HERE -> HereProvider(context)
WeatherProviders.BRIGHT_SKY -> BrightskyProvider(context)
else -> MetNoProvider(context)
}.takeIf { it.isAvailable() }
}

View File

@ -0,0 +1,38 @@
package de.mm20.launcher2.weather.brightsky
import com.google.gson.annotations.SerializedName
import retrofit2.http.GET
import retrofit2.http.Query
data class BrightSkyResult(
val weather: Array<BrightSkyResultWether>
)
data class BrightSkyResultWether(
val timestamp: String?,
@SerializedName("source_id") val sourceId: Int?,
@SerializedName("cloud_cover") val cloudCover: Double?,
val condition: String?,
@SerializedName("dew_point") val dewPoint: Double?,
val icon: String?,
val precipitation: Double?,
@SerializedName("pressure_msl") val pressureMsl: Double?,
@SerializedName("relative_humidity") val relativeHumidity: Double?,
val sunshine: Double?,
val temperature: Double?,
val visibility: Double?,
@SerializedName("wind_direction") val windDirection: Double?,
@SerializedName("wind_speed") val windSpeed: Double?,
@SerializedName("wind_gust_direction") val windGustDirection: Double?,
@SerializedName("wind_gust_speed") val windGustSpeed: Double?,
)
interface BrightSkyApi {
@GET("/weather?units=si")
suspend fun weather(
@Query("date") date: String,
@Query("last_date") lastDate: String,
@Query("lat") lat: Double,
@Query("lon") lon: Double,
): BrightSkyResult
}

View File

@ -0,0 +1,129 @@
package de.mm20.launcher2.weather.brightsky
import android.content.Context
import android.content.SharedPreferences
import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.weather.*
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create
import java.lang.Exception
import kotlin.math.roundToInt
class BrightskyProvider(
override val context: Context
) : LatLonWeatherProvider() {
val apiClient by lazy {
val retrofit = Retrofit.Builder()
.baseUrl("https://api.brightsky.dev/")
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create<BrightSkyApi>()
}
override suspend fun loadWeatherData(location: LatLonWeatherLocation): WeatherUpdateResult<LatLonWeatherLocation>? {
val result = runCatching {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
val date = Calendar.getInstance()
date.timeInMillis -= 1000 * 60 * 30
val startDate = format.format(date.timeInMillis)
date.timeInMillis += 1000 * 60 * 60 * 24 * 14
val endDate = format.format(date.timeInMillis)
apiClient.weather(
date = startDate,
lastDate = endDate,
lat = location.lat,
lon = location.lon,
)
}.getOrElse {
CrashReporter.logException(Exception(it))
return null
}
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
val forecasts = mutableListOf<Forecast>()
val updateTime = System.currentTimeMillis()
for (weather in result.weather) {
forecasts.add(
Forecast(
timestamp = format.parse(weather.timestamp)?.time ?: continue,
clouds = weather.cloudCover?.roundToInt() ?: -1,
condition = getCondition(weather.icon ?: continue) ?: continue,
humidity = weather.relativeHumidity ?: -1.0,
icon = getIcon(weather.icon) ?: continue,
location = location.name,
maxTemp = weather.temperature ?: continue,
minTemp = weather.temperature,
night = (weather.sunshine ?: 100.0).roundToInt() == 0,
pressure = weather.pressureMsl ?: -1.0,
provider = "Deutscher Wetterdienst",
providerUrl = "https://www.dwd.de/",
precipitation = weather.precipitation ?: -1.0,
precipProbability = -1,
temperature = weather.temperature,
updateTime = updateTime,
windDirection = weather.windDirection ?: -1.0,
windSpeed = weather.windSpeed ?: -1.0
)
)
}
return WeatherUpdateResult(
forecasts, location
)
}
private fun getIcon(icon: String): Int? {
return when (icon) {
"clear-day", "clear-night" -> Forecast.CLEAR
"partly-cloudy-day", "partly-cloudy-night" -> Forecast.PARTLY_CLOUDY
"cloudy" -> Forecast.CLOUDY
"fog" -> Forecast.FOG
"wind" -> Forecast.WIND
"rain" -> Forecast.SHOWERS
"sleet" -> Forecast.SLEET
"snow" -> Forecast.SNOW
"hail" -> Forecast.HAIL
"thunderstorm" -> Forecast.THUNDERSTORM
else -> null
}
}
private fun getCondition(icon: String): String? {
val resId = when (icon) {
"clear-day", "clear-night" -> R.string.weather_clearsky
"partly-cloudy-day", "partly-cloudy-night" -> R.string.weather_partlycloudy
"cloudy" -> R.string.weather_cloudy
"fog" -> R.string.weather_fog
"wind" -> R.string.weather_wind
"rain" -> R.string.weather_rain
"sleet" -> R.string.weather_sleet
"snow" -> R.string.weather_snow
"hail" -> R.string.weather_hail
"thunderstorm" -> R.string.weather_thunder
else -> return null
}
return context.getString(resId)
}
override val preferences: SharedPreferences
get() = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
override fun isUpdateRequired(): Boolean {
return getLastUpdate() + 3600000 < System.currentTimeMillis()
}
override fun isAvailable(): Boolean {
return true
}
override val name: String
get() = context.getString(R.string.provider_brightsky)
companion object {
const val PREFS = "bright_sky"
}
}