Rewrite Weather class in Kotlin and extract cache handling and mapping methods

Cache Handling and static mapping methods are extracted to new classes

Also adds test for weather mapping and moves legacy Weather class there to ensure the conversion to kotlin did not introduce errors
This commit is contained in:
Daniele Gobbetti
2025-07-20 11:50:56 +02:00
committed by José Rebelo
parent 6704f1f39a
commit 5b35bbba8f
42 changed files with 1891 additions and 1320 deletions
@@ -87,6 +87,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherCacheManager;
import nodomain.freeyourgadget.gadgetbridge.service.NotificationCollectorMonitorService;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
@@ -310,7 +311,7 @@ public class GBApplication extends Application {
setupExceptionHandler(prefs.getBoolean("crash_notification", isDebug()));
Weather.getInstance().setCacheFile(getCacheDir(), prefs.getBoolean("cache_weather", true));
Weather.INSTANCE.initializeCache(new WeatherCacheManager(getCacheDir(), prefs.getBoolean("cache_weather", true)));
deviceManager = new DeviceManager(this);
String language = prefs.getString("language", "default");
@@ -124,6 +124,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -362,13 +363,13 @@ public class DebugActivity extends AbstractGBActivity {
setWeatherButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (Weather.getInstance().getWeatherSpec() == null) {
if (Weather.INSTANCE.getWeatherSpec() == null) {
final WeatherSpec weatherSpec = new WeatherSpec();
weatherSpec.forecasts = new ArrayList<>();
weatherSpec.location = "Green Hill";
weatherSpec.currentConditionCode = 601; // snow
weatherSpec.currentCondition = Weather.getConditionString(DebugActivity.this, weatherSpec.currentConditionCode);
weatherSpec.currentCondition = WeatherMapper.INSTANCE.getConditionString(DebugActivity.this, weatherSpec.currentConditionCode);
weatherSpec.currentTemp = 15 + 273;
weatherSpec.currentHumidity = 30;
@@ -388,10 +389,10 @@ public class DebugActivity extends AbstractGBActivity {
weatherSpec.forecasts.add(gbForecast);
}
Weather.getInstance().setWeatherSpec(new ArrayList<>(Collections.singletonList(weatherSpec)));
Weather.INSTANCE.setWeatherSpec(new ArrayList<>(Collections.singletonList(weatherSpec)));
}
final ArrayList<WeatherSpec> specs = new ArrayList<>(Weather.getInstance().getWeatherSpecs());
final ArrayList<WeatherSpec> specs = new ArrayList<>(Weather.INSTANCE.getWeatherSpecs());
GBApplication.deviceService().onSendWeather(specs);
}
});
@@ -400,7 +401,7 @@ public class DebugActivity extends AbstractGBActivity {
showCachedWeatherButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
final List<WeatherSpec> weatherSpecs = Weather.getInstance().getWeatherSpecs();
final List<WeatherSpec> weatherSpecs = Weather.INSTANCE.getWeatherSpecs();
if (weatherSpecs == null || weatherSpecs.isEmpty()) {
displayWeatherInfo(null);
@@ -69,6 +69,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.maps.MapsSettingsActivity
import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter;
import nodomain.freeyourgadget.gadgetbridge.externalevents.TimeChangeReceiver;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherCacheManager;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -168,7 +169,7 @@ public class SettingsActivity extends AbstractSettingsActivityV2 {
pref.setOnPreferenceChangeListener((preference, newVal) -> {
boolean doEnable = Boolean.TRUE.equals(newVal);
Weather.getInstance().setCacheFile(requireContext().getCacheDir(), doEnable);
Weather.INSTANCE.initializeCache(new WeatherCacheManager(requireContext().getCacheDir(), doEnable));
return true;
});
@@ -38,6 +38,7 @@ import cyanogenmod.weather.util.WeatherUtils;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@@ -158,8 +159,8 @@ public class CMWeatherReceiver extends BroadcastReceiver implements CMWeatherMan
}
weatherSpec.windDirection = (int) weatherInfo.getWindDirection();
weatherSpec.currentConditionCode = Weather.mapToOpenWeatherMapCondition(CMtoYahooCondintion(weatherInfo.getConditionCode()));
weatherSpec.currentCondition = Weather.getConditionString(mContext, weatherSpec.currentConditionCode);
weatherSpec.currentConditionCode = WeatherMapper.INSTANCE.mapToOpenWeatherMapCondition(CMtoYahooCondintion(weatherInfo.getConditionCode()));
weatherSpec.currentCondition = WeatherMapper.INSTANCE.getConditionString(mContext, weatherSpec.currentConditionCode);
weatherSpec.currentHumidity = (int) weatherInfo.getHumidity();
weatherSpec.forecasts = new ArrayList<>();
@@ -174,11 +175,11 @@ public class CMWeatherReceiver extends BroadcastReceiver implements CMWeatherMan
gbForecast.maxTemp = (int) cmForecast.getHigh() + 273;
gbForecast.minTemp = (int) cmForecast.getLow() + 273;
}
gbForecast.conditionCode = Weather.mapToOpenWeatherMapCondition(CMtoYahooCondintion(cmForecast.getConditionCode()));
gbForecast.conditionCode = WeatherMapper.INSTANCE.mapToOpenWeatherMapCondition(CMtoYahooCondintion(cmForecast.getConditionCode()));
weatherSpec.forecasts.add(gbForecast);
}
ArrayList<WeatherSpec> weatherSpecs = new ArrayList<>(Collections.singletonList(weatherSpec));
Weather.getInstance().setWeatherSpec(weatherSpecs);
Weather.INSTANCE.setWeatherSpec(weatherSpecs);
GBApplication.deviceService().onSendWeather(weatherSpecs);
} else {
LOG.info("request has returned null for WeatherInfo");
@@ -84,7 +84,7 @@ public class GenericWeatherReceiver extends BroadcastReceiver {
}
LOG.info("Got generic weather for {} locations", weathers.size());
Weather.getInstance().setWeatherSpec(weathers);
Weather.INSTANCE.setWeatherSpec(weathers);
GBApplication.deviceService().onSendWeather(weathers);
} catch (final Exception e) {
GB.toast("Gadgetbridge received broken or incompatible weather data", Toast.LENGTH_SHORT, GB.ERROR, e);
@@ -50,6 +50,7 @@ import lineageos.weather.util.WeatherUtils;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@@ -183,8 +184,8 @@ public class LineageOsWeatherReceiver extends BroadcastReceiver implements Linea
}
weatherSpec.windDirection = (int) weatherInfo.getWindDirection();
weatherSpec.currentConditionCode = Weather.mapToOpenWeatherMapCondition(LineageOSToYahooCondition(weatherInfo.getConditionCode()));
weatherSpec.currentCondition = Weather.getConditionString(mContext, weatherSpec.currentConditionCode);
weatherSpec.currentConditionCode = WeatherMapper.INSTANCE.mapToOpenWeatherMapCondition(LineageOSToYahooCondition(weatherInfo.getConditionCode()));
weatherSpec.currentCondition = WeatherMapper.INSTANCE.getConditionString(mContext, weatherSpec.currentConditionCode);
weatherSpec.currentHumidity = (int) weatherInfo.getHumidity();
weatherSpec.forecasts = new ArrayList<>();
@@ -199,11 +200,11 @@ public class LineageOsWeatherReceiver extends BroadcastReceiver implements Linea
gbForecast.maxTemp = (int) cmForecast.getHigh() + 273;
gbForecast.minTemp = (int) cmForecast.getLow() + 273;
}
gbForecast.conditionCode = Weather.mapToOpenWeatherMapCondition(LineageOSToYahooCondition(cmForecast.getConditionCode()));
gbForecast.conditionCode = WeatherMapper.INSTANCE.mapToOpenWeatherMapCondition(LineageOSToYahooCondition(cmForecast.getConditionCode()));
weatherSpec.forecasts.add(gbForecast);
}
ArrayList<WeatherSpec> weatherSpecs = new ArrayList<>(Collections.singletonList(weatherSpec));
Weather.getInstance().setWeatherSpec(weatherSpecs);
Weather.INSTANCE.setWeatherSpec(weatherSpecs);
GBApplication.deviceService().onSendWeather(weatherSpecs);
} else {
LOG.info("request has returned null for WeatherInfo");
@@ -34,6 +34,7 @@ import java.util.Collections;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
@@ -113,8 +114,8 @@ public class OmniJawsObserver extends ContentObserver {
if (i == 0) {
weatherSpec.location = c.getString(0);
weatherSpec.currentConditionCode = Weather.mapToOpenWeatherMapCondition(c.getInt(2));
weatherSpec.currentCondition = Weather.getConditionString(mContext, weatherSpec.currentConditionCode);
weatherSpec.currentConditionCode = WeatherMapper.INSTANCE.mapToOpenWeatherMapCondition(c.getInt(2));
weatherSpec.currentCondition = WeatherMapper.INSTANCE.getConditionString(mContext, weatherSpec.currentConditionCode);
//alternatively the following would also be possible
//weatherSpec.currentCondition = c.getString(1);
@@ -132,14 +133,14 @@ public class OmniJawsObserver extends ContentObserver {
WeatherSpec.Daily gbForecast = new WeatherSpec.Daily();
gbForecast.minTemp = toKelvin(c.getFloat(5));
gbForecast.maxTemp = toKelvin(c.getFloat(6));
gbForecast.conditionCode = Weather.mapToOpenWeatherMapCondition(c.getInt(8));
gbForecast.conditionCode = WeatherMapper.INSTANCE.mapToOpenWeatherMapCondition(c.getInt(8));
weatherSpec.forecasts.add(gbForecast);
}
}
}
ArrayList<WeatherSpec> weatherSpecs = new ArrayList<>(Collections.singletonList(weatherSpec));
Weather.getInstance().setWeatherSpec(weatherSpecs);
Weather.INSTANCE.setWeatherSpec(weatherSpecs);
GBApplication.deviceService().onSendWeather(weatherSpecs);
} finally {
@@ -43,7 +43,7 @@ public class TinyWeatherForecastGermanyReceiver extends BroadcastReceiver {
if (weatherSpec != null) {
ArrayList<WeatherSpec> weatherSpecs = new ArrayList<>(Collections.singletonList(weatherSpec));
weatherSpec.timestamp = (int) (System.currentTimeMillis() / 1000);
Weather.getInstance().setWeatherSpec(weatherSpecs);
Weather.INSTANCE.setWeatherSpec(weatherSpecs);
GBApplication.deviceService().onSendWeather(weatherSpecs);
}
} catch (Exception e) {
@@ -50,14 +50,14 @@ public class WeatherNotificationReceiver extends BroadcastReceiver {
}
if (parcelableWeather2 != null) {
Weather weather = Weather.getInstance();
Weather weather = Weather.INSTANCE;
weather.setReconstructedOWMForecast(parcelableWeather2.reconstructedOWMForecast);
WeatherSpec weatherSpec = parcelableWeather2.weatherSpec;
LOG.info("weather in " + weatherSpec.location + " is " + weatherSpec.currentCondition + " (" + (weatherSpec.currentTemp - 273) + "°C)");
ArrayList<WeatherSpec> weatherSpecs = new ArrayList<>(Collections.singletonList(weatherSpec));
Weather.getInstance().setWeatherSpec(weatherSpecs);
Weather.INSTANCE.setWeatherSpec(weatherSpecs);
GBApplication.deviceService().onSendWeather(weatherSpecs);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,79 @@
package nodomain.freeyourgadget.gadgetbridge.model
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.util.concurrent.CopyOnWriteArrayList
object Weather {
private val LOG = LoggerFactory.getLogger("Weather")
private val weatherSpecs = CopyOnWriteArrayList<WeatherSpec>()
private var reconstructedOWMForecast: JSONObject? = null
private var cacheManager: WeatherCacheManager? = null
fun setWeatherSpec(newSpecs: Collection<WeatherSpec>) {
weatherSpecs.apply {
clear()
addAll(newSpecs)
}
cacheManager?.save(weatherSpecs)
}
fun getWeatherSpec(): WeatherSpec? = weatherSpecs.firstOrNull()
fun getWeatherSpecs(): List<WeatherSpec> = weatherSpecs
fun createReconstructedOWMWeatherReply(): JSONObject? {
val spec = getWeatherSpec() ?: return null
return try {
JSONObject().apply {
put("weather", JSONArray().apply {
put(JSONObject().apply {
put("id", spec.currentConditionCode)
put("main", spec.currentCondition)
put("description", spec.currentCondition)
put("icon", WeatherMapper.mapToOpenWeatherMapIcon(spec.currentConditionCode))
})
})
put("main", JSONObject().apply {
put("temp", spec.currentTemp)
put("humidity", spec.currentHumidity)
put("temp_min", spec.todayMinTemp)
put("temp_max", spec.todayMaxTemp)
})
put("wind", JSONObject().apply {
put("speed", spec.windSpeed / 3.6f)
put("deg", spec.windDirection)
})
put("name", spec.location)
}.also {
LOG.debug("Weather JSON for WEBVIEW: {}", it)
}
} catch (e: JSONException) {
LOG.error("Error while reconstructing OWM weather reply", e)
null
}
}
fun getReconstructedOWMForecast(): JSONObject? = reconstructedOWMForecast
fun setReconstructedOWMForecast(value: JSONObject?) {
reconstructedOWMForecast = value
}
fun initializeCache(cacheManager: WeatherCacheManager) {
this.cacheManager = cacheManager
cacheManager.load { loadedSpecs ->
weatherSpecs.clear()
weatherSpecs.addAll(loadedSpecs)
}
}
}
@@ -0,0 +1,59 @@
package nodomain.freeyourgadget.gadgetbridge.model
import kotlinx.coroutines.*
import org.slf4j.LoggerFactory
import java.io.*
class WeatherCacheManager(
cacheDir: File,
private val useCache: Boolean
) {
private val LOG = LoggerFactory.getLogger("WeatherCacheManager")
private val cacheFile = File(cacheDir, "weatherCache.bin")
fun load(onLoaded: (List<WeatherSpec>) -> Unit) {
if (!useCache || !cacheFile.exists()) return
CoroutineScope(Dispatchers.IO).launch {
try {
ObjectInputStream(FileInputStream(cacheFile)).use { input ->
val specs = input.readObject() as? ArrayList<WeatherSpec>
specs?.let { onLoaded(it) }
LOG.info("Loaded ${specs?.size ?: 0} weather specs from cache")
}
} catch (e: Exception) {
LOG.error("Failed to load weather cache", e)
}
}
}
fun save(specs: List<WeatherSpec>) {
if (!useCache || specs.isEmpty()) return
CoroutineScope(Dispatchers.IO).launch {
try {
ObjectOutputStream(FileOutputStream(cacheFile)).use { output ->
output.writeObject(ArrayList(specs))
}
LOG.info("Saved weather specs to cache: ${cacheFile.path}")
} catch (e: Exception) {
LOG.error("Failed to save weather cache", e)
}
}
}
fun clear() {
if (cacheFile.exists()) {
try {
if (cacheFile.delete()) {
LOG.info("Deleted cache file: ${cacheFile.path}")
} else {
LOG.warn("Failed to delete cache file: ${cacheFile.path}")
}
} catch (e: Exception) {
LOG.error("Error deleting cache file", e)
}
}
}
}
@@ -0,0 +1,404 @@
package nodomain.freeyourgadget.gadgetbridge.model
import android.content.Context
import nodomain.freeyourgadget.gadgetbridge.R
object WeatherMapper {
fun mapToOpenWeatherMapIcon(code: Int): String = when {
//see https://openweathermap.org/weather-conditions
code in 200..299 -> "11d"
code in 300..499 -> "09d"
code in 500..509 -> "10d"
code in 511..599 -> "09d"
code in 600..699 -> "13d"
code in 700..799 -> "50d"
code == 800 -> "01d" //TODO: night?
code == 801 -> "02d" //TODO: night?
code == 802 -> "03d" //TODO: night?
code == 803 || code == 804 -> "04d" //TODO: night?
else -> "02d" // fallback
}
fun mapToOpenWeatherMapCondition(yahooCondition: Int): Int {
//yahoo weather conditions:
//https://developer.yahoo.com/weather/documentation.html
return when (yahooCondition) {
0 -> 900
1 -> 901
2 -> 962
3 -> 212
4 -> 211
5, 6 -> 616
7 -> 600
8, 9 -> 301
10 -> 511
11, 12 -> 521
13, 14 -> 620
15, 41, 42, 43, 46 -> 602
16 -> 601
17, 35 -> 906
18 -> 611
19 -> 761
20 -> 741
21 -> 721
22 -> 711
23, 24 -> 905
25 -> 903
26, 27, 28 -> 804
29, 30 -> 801
31, 32 -> 800
33, 34 -> 801
36 -> 904
37, 38, 39 -> 210
40 -> 520
44 -> 801
45, 47 -> 211
3200 -> -1
else -> -1
}
}
fun getConditionString(context: Context, code: Int): String = when (code) {
200 -> context.getString(R.string.weather_condition_thunderstorm_with_light_rain)
201 -> context.getString(R.string.weather_condition_thunderstorm_with_rain)
202 -> context.getString(R.string.weather_condition_thunderstorm_with_heavy_rain)
210 -> context.getString(R.string.weather_condition_light_thunderstorm)
211 -> context.getString(R.string.weather_condition_thunderstorm)
230 -> context.getString(R.string.weather_condition_thunderstorm_with_light_drizzle)
231 -> context.getString(R.string.weather_condition_thunderstorm_with_drizzle)
232 -> context.getString(R.string.weather_condition_thunderstorm_with_heavy_drizzle)
212 -> context.getString(R.string.weather_condition_heavy_thunderstorm)
221 -> context.getString(R.string.weather_condition_ragged_thunderstorm)
// Group 3xx: Drizzle
300 -> context.getString(R.string.weather_condition_light_intensity_drizzle)
301 -> context.getString(R.string.weather_condition_drizzle)
302 -> context.getString(R.string.weather_condition_heavy_intensity_drizzle)
310 -> context.getString(R.string.weather_condition_light_intensity_drizzle_rain)
311 -> context.getString(R.string.weather_condition_drizzle_rain)
312 -> context.getString(R.string.weather_condition_heavy_intensity_drizzle_rain)
313 -> context.getString(R.string.weather_condition_shower_rain_and_drizzle)
314 -> context.getString(R.string.weather_condition_heavy_shower_rain_and_drizzle)
321 -> context.getString(R.string.weather_condition_shower_drizzle)
// Group 5xx: Rain
500 -> context.getString(R.string.weather_condition_light_rain)
501 -> context.getString(R.string.weather_condition_moderate_rain)
502 -> context.getString(R.string.weather_condition_heavy_intensity_rain)
503 -> context.getString(R.string.weather_condition_very_heavy_rain)
504 -> context.getString(R.string.weather_condition_extreme_rain)
511 -> context.getString(R.string.weather_condition_freezing_rain)
520 -> context.getString(R.string.weather_condition_light_intensity_shower_rain)
521 -> context.getString(R.string.weather_condition_shower_rain)
522 -> context.getString(R.string.weather_condition_heavy_intensity_shower_rain)
531 -> context.getString(R.string.weather_condition_ragged_shower_rain)
// Group 6xx: Snow
600 -> context.getString(R.string.weather_condition_light_snow)
601 -> context.getString(R.string.weather_condition_snow)
602 -> context.getString(R.string.weather_condition_heavy_snow)
611 -> context.getString(R.string.weather_condition_sleet)
612 -> context.getString(R.string.weather_condition_shower_sleet)
615 -> context.getString(R.string.weather_condition_light_rain_and_snow)
616 -> context.getString(R.string.weather_condition_rain_and_snow)
620 -> context.getString(R.string.weather_condition_light_shower_snow)
621 -> context.getString(R.string.weather_condition_shower_snow)
622 -> context.getString(R.string.weather_condition_heavy_shower_snow)
// Group 7xx: Atmosphere
701 -> context.getString(R.string.weather_condition_mist)
711 -> context.getString(R.string.weather_condition_smoke)
721 -> context.getString(R.string.weather_condition_haze)
731 -> context.getString(R.string.weather_condition_sandcase_dust_whirls)
741 -> context.getString(R.string.weather_condition_fog)
751 -> context.getString(R.string.weather_condition_sand)
761 -> context.getString(R.string.weather_condition_dust)
762 -> context.getString(R.string.weather_condition_volcanic_ash)
771 -> context.getString(R.string.weather_condition_squalls)
781, 900 -> context.getString(R.string.weather_condition_tornado)
// Group 80x: Clouds
800 -> context.getString(R.string.weather_condition_clear_sky)
801 -> context.getString(R.string.weather_condition_few_clouds)
802 -> context.getString(R.string.weather_condition_scattered_clouds)
803 -> context.getString(R.string.weather_condition_broken_clouds)
804 -> context.getString(R.string.weather_condition_overcast_clouds)
// Group 90x: Extreme
901 -> context.getString(R.string.weather_condition_tropical_storm)
902, 962 -> context.getString(R.string.weather_condition_hurricane)
903 -> context.getString(R.string.weather_condition_cold)
904 -> context.getString(R.string.weather_condition_hot)
905 -> context.getString(R.string.weather_condition_windy)
906 -> context.getString(R.string.weather_condition_hail)
// Group 9xx: Additional
951 -> context.getString(R.string.weather_condition_calm)
952 -> context.getString(R.string.weather_condition_light_breeze)
953 -> context.getString(R.string.weather_condition_gentle_breeze)
954 -> context.getString(R.string.weather_condition_moderate_breeze)
955 -> context.getString(R.string.weather_condition_fresh_breeze)
956 -> context.getString(R.string.weather_condition_strong_breeze)
957 -> context.getString(R.string.weather_condition_high_windcase_near_gale)
958 -> context.getString(R.string.weather_condition_gale)
959 -> context.getString(R.string.weather_condition_severe_gale)
960 -> context.getString(R.string.weather_condition_storm)
961 -> context.getString(R.string.weather_condition_violent_storm)
else -> ""
}
fun getAqiLevelString(context: Context, aqi: Int): String = when {
// Uses the [2023 Plume index](https://plumelabs.files.wordpress.com/2023/06/plume_aqi_2023.pdf) as a reference
aqi < 0 -> context.getString(R.string.aqi_level_unknown)
aqi < 20 -> context.getString(R.string.aqi_level_excellent)
aqi < 50 -> context.getString(R.string.aqi_level_fair)
aqi < 100 -> context.getString(R.string.aqi_level_poor)
aqi < 150 -> context.getString(R.string.aqi_level_unhealthy)
aqi < 250 -> context.getString(R.string.aqi_level_very_unhealthy)
else -> context.getString(R.string.aqi_level_dangerous)
}
fun mapToPebbleCondition(openWeatherMapCondition: Int): Byte {
/* deducted values:
0 = sun + cloud
1 = clouds
2 = some snow
3 = some rain
4 = heavy rain
5 = heavy snow
6 = sun + cloud + rain (default icon?)
7 = sun
8 = rain + snow
9 = 6
10, 11, ... = empty icon
*/
return when (openWeatherMapCondition) {
200, 201, 202, 210, 211, 230, 231, 232, 212, 221 -> 4
300, 301, 302, 310, 311, 312, 313, 314, 321, 500, 501 -> 3
502, 503, 504, 511, 520, 521, 522, 531 -> 4
600, 601, 620 -> 2
602, 611, 612, 621, 622 -> 5
615, 616 -> 8
701, 711, 721, 731, 741, 751, 761, 762, 771, 781, 900 -> 6
800 -> 7
801, 802, 803, 804 -> 0
901, 903, 904, 905, 906, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 902, 962 -> 6
else -> 6
}
}
fun mapToYahooCondition(openWeatherMapCondition: Int): Int {
// openweathermap.org conditions:
// http://openweathermap.org/weather-conditions
return when (openWeatherMapCondition) {
200, 201, 202, 210, 211, 230, 231, 232 -> 4
212, 221 -> 3
300, 301, 302, 310, 311, 312 -> 9
313, 314, 321 -> 11
500, 501, 502, 503, 504, 511 -> 10
520 -> 40
521, 522, 531 -> 12
600 -> 7
601 -> 16
602 -> 15
611, 612 -> 18
615, 616 -> 5
620 -> 14
621 -> 46
622, 701, 711 -> 22
721 -> 21
731 -> 3200
741 -> 20
751, 761 -> 19
762, 771 -> 3200
781, 900 -> 0
800 -> 32
801, 802 -> 34
803, 804 -> 44
901 -> 1
903 -> 25
904 -> 36
905 -> 24
906 -> 17
951, 952, 953, 954, 955 -> 34
956, 957 -> 24
958, 959, 960, 961 -> 3200
902, 962 -> 2
else -> 3200
}
}
fun mapToZeTimeConditionOld(openWeatherMapCondition: Int): Byte {
/* deducted values:
0 = partly cloudy
1 = cloudy
2 = sunny
3 = windy/gale
4 = heavy rain
5 = snowy
6 = storm
*/
return when (openWeatherMapCondition) {
200, 201, 202, 210, 211, 230, 231, 232, 212, 221, 771, 781, 900, 901, 960, 961, 902, 962 -> 6
300, 301, 302, 310, 311, 312, 313, 314, 321, 500, 501, 502, 503, 504, 511, 520, 521, 522, 531, 906 -> 4
600, 601, 620, 602, 611, 612, 621, 622, 615, 616, 903 -> 5
701, 711, 721, 731, 741, 751, 761, 762 -> 1
800, 904 -> 2
801, 802, 803, 804 -> 0
905, 951, 952, 953, 954, 955, 956, 957, 958, 959 -> 3
else -> 0
}
}
fun mapToZeTimeCondition(openWeatherMapCondition: Int): Byte {
/* deducted values:
0 = tornado
1 = typhoon
2 = hurricane
3 = thunderstorm
4 = rain and snow
5 = unavailable
6 = freezing rain
7 = drizzle
8 = showers
9 = snow flurries
10 = blowing snow
11 = snow
12 = sleet
13 = foggy
14 = windy
15 = cloudy
16 = partly cloudy (night)
17 = partly cloudy (day)
18 = clear night
19 = sunny
20 = thundershower
21 = hot
22 = scattered thunders
23 = snow showers
24 = heavy snow
*/
return when (openWeatherMapCondition) {
210 -> 22
200, 201, 202, 230, 231, 232 -> 20
211, 212, 221 -> 3
781, 900 -> 0
901 -> 1
771, 960, 961, 902, 962 -> 2
300, 301, 302, 310, 311, 312, 313, 314, 321 -> 7
500, 501, 502, 503, 504, 520, 521, 522, 531, 906 -> 8
511 -> 6
620, 621, 622 -> 23
615, 616 -> 4
611, 612 -> 12
600, 601 -> 11
602 -> 24
701, 711, 721, 731, 741, 751, 761, 762 -> 13
800 -> 19
904 -> 21
801, 802, 803 -> 17
804 -> 15
905, 951, 952, 953, 954, 955, 956, 957, 958, 959 -> 14
903 -> 5
else -> 5
}
}
fun mapToCmfCondition(openWeatherMapCondition: Int): Byte {
/* deducted values:
1 = sunny
2 = cloudy
3 = overcast
4 = showers
5 = snow showers
6 = fog
9 = thunder showers
14 = sleet
19 = hot (extreme)
20 = cold (extreme)
21 = strong wind
22 = (night) sunny - with moon
23 = (night) sunny with stars
24 = (night) cloudy - with moon
25 = sun with haze
26 = cloudy (sun with cloud)
*/
return when (openWeatherMapCondition) {
210, 200, 201, 202, 230, 231, 232, 211, 212, 221 -> 9
901, 781, 900, 771, 960, 961, 902, 962 -> 21
300, 301, 302, 310, 311, 312, 313, 314, 321, 500, 501, 502, 503, 504, 520, 521, 522, 531 -> 4
906, 615, 616, 511 -> 14
611, 612, 600, 601, 602, 620, 621, 622 -> 5
701, 711, 721, 731, 741, 751, 761, 762 -> 6
800 -> 1
904 -> 19
801, 802 -> 26
803 -> 2
804 -> 3
905, 951, 952, 953, 954, 955, 956, 957, 958, 959 -> 21
903 -> 20
else -> 20
}
}
fun mapToFitProCondition(openWeatherMapCondition: Int): Byte {
return when (openWeatherMapCondition) {
100 -> 1
104 -> 2
101, 102, 103 -> 3
305, 309 -> 4
306, 314, 399 -> 5
307, 308, 310, 311, 312, 315, 316, 317, 318 -> 6
300, 301, 302, 303 -> 7
400, 407 -> 8
401, 408, 499 -> 9
402, 403, 409, 410 -> 10
404, 405, 406 -> 11
500, 501, 502, 509, 510, 511, 512, 513, 514, 515 -> 12
304, 313 -> 13
503, 504, 507, 508 -> 14
200, 201, 202, 203, 204 -> 15
205, 206, 207, 208 -> 16
209, 210, 211 -> 17
212 -> 18
231 -> 19
else -> 3
}
}
}
@@ -1985,7 +1985,7 @@ public class BangleJSDeviceSupport extends AbstractBTLESingleDeviceSupport {
private void handleWeather(JSONObject json)
{
if (!json.has("v")) {
handleWeatherV1(Weather.getInstance().getWeatherSpecs());
handleWeatherV1(Weather.INSTANCE.getWeatherSpecs());
return;
}
try {
@@ -1995,9 +1995,9 @@ public class BangleJSDeviceSupport extends AbstractBTLESingleDeviceSupport {
forecast = json.getBoolean("f");
}
if (version == 1) {
handleWeatherV1(Weather.getInstance().getWeatherSpecs());
handleWeatherV1(Weather.INSTANCE.getWeatherSpecs());
} else if (version == 2) {
handleWeatherV2(Weather.getInstance().getWeatherSpecs(), forecast);
handleWeatherV2(Weather.INSTANCE.getWeatherSpecs(), forecast);
}
} catch (JSONException e) {
LOG.info("JSONException: " + e.getLocalizedMessage());
@@ -71,7 +71,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Contact;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLESingleDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
@@ -813,7 +813,7 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
final int payloadLength = (7 * 9) + (24 * 2) + (supportsSunriseSunset ? 32 : 30) + (supportsSunriseSunset ? 7 * 8 : 0);
final ByteBuffer buf = ByteBuffer.allocate(payloadLength).order(ByteOrder.BIG_ENDIAN);
// start with the current day's weather
buf.put(Weather.mapToCmfCondition(weatherSpec.currentConditionCode));
buf.put(WeatherMapper.INSTANCE.mapToCmfCondition(weatherSpec.currentConditionCode));
buf.put((byte) (weatherSpec.currentTemp - 273 + 100)); // convert Kelvin to C, add 100
buf.put((byte) (weatherSpec.todayMaxTemp - 273 + 100)); // convert Kelvin to C, add 100
buf.put((byte) (weatherSpec.todayMinTemp - 273 + 100)); // convert Kelvin to C, add 100
@@ -828,7 +828,7 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
for (int i = 0; i < 6; i++) {
if (i < maxForecastsAvailable) {
WeatherSpec.Daily forecastDay = weatherSpec.forecasts.get(i);
buf.put((byte) (Weather.mapToCmfCondition(forecastDay.conditionCode))); // weather condition flag
buf.put((byte) (WeatherMapper.INSTANCE.mapToCmfCondition(forecastDay.conditionCode))); // weather condition flag
buf.put((byte) (forecastDay.maxTemp - 273 + 100)); // temp in C (not shown in future days' forecasts)
buf.put((byte) (forecastDay.maxTemp - 273 + 100)); // max temp in C, + 100
buf.put((byte) (forecastDay.minTemp - 273 + 100)); // min temp in C, + 100
@@ -858,7 +858,7 @@ public class CmfWatchProSupport extends AbstractBTLESingleDeviceSupport implemen
buf.put((byte) forecastHr.conditionCode); // condition
} else {
buf.put((byte) (weatherSpec.currentTemp - 273 + 100)); // assume current temp
buf.put((byte) (Weather.mapToCmfCondition(weatherSpec.currentConditionCode))); // current condition
buf.put((byte) (WeatherMapper.INSTANCE.mapToCmfCondition(weatherSpec.currentConditionCode))); // current condition
}
}
// place name - watch scrolls after ~10 chars. Pad up to 32 bytes.
@@ -356,7 +356,7 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
// Pull the weather into a local variable so that if it changes between the two lenses being
// updated, we won't end up with a skewed value.
@Nullable WeatherSpec weather = Weather.getInstance().getWeatherSpec();
@Nullable WeatherSpec weather = Weather.INSTANCE.getWeatherSpec();
// Run in the background in case the command hangs and this was run from the UI thread.
backgroundTasksHandler.post(() -> {
@@ -136,7 +136,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLESingleDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
@@ -556,7 +556,7 @@ public class FitProDeviceSupport extends AbstractBTLESingleDeviceSupport {
weatherUnit = 1;
}
byte currentConditionCode = Weather.mapToFitProCondition(weatherSpec.currentConditionCode);
byte currentConditionCode = WeatherMapper.INSTANCE.mapToFitProCondition(weatherSpec.currentConditionCode);
TransactionBuilder builder = createTransactionBuilder("weather");
writeChunkedData(builder, craftData(CMD_GROUP_GENERAL, CMD_WEATHER, new byte[]{(byte) todayMin, (byte) todayMax, (byte) currentConditionCode, (byte) weatherUnit}));
builder.queue();
@@ -284,7 +284,7 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC
@Override
public void evaluateGBDeviceEvent(GBDeviceEvent deviceEvent) {
if (deviceEvent instanceof WeatherRequestDeviceEvent) {
WeatherSpec weather = Weather.getInstance().getWeatherSpec();
WeatherSpec weather = Weather.INSTANCE.getWeatherSpec();
if (weather != null) {
sendWeatherConditions(weather);
}
@@ -23,6 +23,7 @@ import java.util.Map;
import lineageos.weather.util.WeatherUtils;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.CurrentPosition;
@@ -39,7 +40,7 @@ public class WeatherHandler {
final String path = request.getPath();
final Map<String, String> query = request.getQuery();
final WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
final WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
LOG.warn("No weather in weather instance");
@@ -160,8 +161,8 @@ public class WeatherHandler {
public WeatherForecastDay(final GregorianCalendar date, final WeatherSpec.Daily dailyForecast, final String tempUnit, final String speedUnit) {
dayOfWeek = BLETypeConversions.dayOfWeekToRawBytes(date);
description = Weather.getConditionString(GBApplication.getContext(), dailyForecast.conditionCode);
summary = Weather.getConditionString(GBApplication.getContext(), dailyForecast.conditionCode);
description = WeatherMapper.INSTANCE.getConditionString(GBApplication.getContext(), dailyForecast.conditionCode);
summary = WeatherMapper.INSTANCE.getConditionString(GBApplication.getContext(), dailyForecast.conditionCode);
high = getTemperature(dailyForecast.maxTemp, tempUnit);
low = getTemperature(dailyForecast.minTemp, tempUnit);
precipProb = dailyForecast.precipProbability;
@@ -212,7 +213,7 @@ public class WeatherHandler {
public WeatherForecastHour(final WeatherSpec.Hourly hourlyForecast, final String tempUnit, final String speedUnit) {
epochSeconds = hourlyForecast.timestamp;
description = Weather.getConditionString(GBApplication.getContext(), hourlyForecast.conditionCode);
description = WeatherMapper.INSTANCE.getConditionString(GBApplication.getContext(), hourlyForecast.conditionCode);
temp = getTemperature(hourlyForecast.temp, tempUnit);
precipProb = hourlyForecast.precipProbability;
wind = new Wind(getSpeed(hourlyForecast.windSpeed, speedUnit), hourlyForecast.windDirection);
@@ -135,7 +135,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.Reminder;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WorldClock;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
@@ -2877,7 +2877,7 @@ public abstract class HuamiSupport extends AbstractBTLESingleDeviceSupport
builder = performInitialized("Sending air quality index");
int length = 8;
int aqi = weatherSpec.airQuality != null ? weatherSpec.airQuality.aqi : -1;
String aqiString = Weather.getAqiLevelString(getContext(), aqi);
String aqiString = WeatherMapper.INSTANCE.getAqiLevelString(getContext(), aqi);
if (supportsConditionString) {
length += aqiString.getBytes().length + 1;
}
@@ -2916,7 +2916,7 @@ public abstract class HuamiSupport extends AbstractBTLESingleDeviceSupport
bytesPerDay = 5;
conditionsLength = weatherSpec.currentCondition.getBytes().length;
for (WeatherSpec.Daily forecast : weatherSpec.forecasts) {
conditionsLength += Weather.getConditionString(getContext(), forecast.conditionCode).getBytes().length;
conditionsLength += WeatherMapper.INSTANCE.getConditionString(getContext(), forecast.conditionCode).getBytes().length;
}
}
@@ -2963,7 +2963,7 @@ public abstract class HuamiSupport extends AbstractBTLESingleDeviceSupport
buf.put((byte) forecastMinTemp);
if (supportsConditionString) {
buf.put(Weather.getConditionString(getContext(), forecast.conditionCode).getBytes());
buf.put(WeatherMapper.INSTANCE.getConditionString(getContext(), forecast.conditionCode).getBytes());
buf.put((byte) 0);
}
}
@@ -91,7 +91,7 @@ public class ZeppOsWeatherHandler {
}
public Response handleHttpRequest(final String path, final Map<String, String> query) {
final WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
final WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
LOG.error("No weather in weather instance");
@@ -277,7 +277,7 @@ public class HuaweiWeatherManager {
public void handleAsyncMessage(HuaweiPacket response) {
if (response.getTlv().getInteger(0x7f, -1) == 0x000186AA) {
// Send weather
final ArrayList<WeatherSpec> specs = new ArrayList<>(nodomain.freeyourgadget.gadgetbridge.model.Weather.getInstance().getWeatherSpecs());
final ArrayList<WeatherSpec> specs = new ArrayList<>(nodomain.freeyourgadget.gadgetbridge.model.Weather.INSTANCE.getWeatherSpecs());
if (specs.isEmpty()) {
LOG.debug("Weather specs empty, returning that weather is disabled.");
try {
@@ -446,8 +446,8 @@ public class MoyoungDeviceSupport extends AbstractBTLESingleDeviceSupport {
if (packetType == MoyoungConstants.CMD_NOTIFY_WEATHER_CHANGE) {
LOG.info("Will transmit cached weather (if any) since the watch asks for it");
if (Weather.getInstance().getWeatherSpec() != null) {
final ArrayList<WeatherSpec> specs = new ArrayList<>(Weather.getInstance().getWeatherSpecs());
if (Weather.INSTANCE.getWeatherSpec() != null) {
final ArrayList<WeatherSpec> specs = new ArrayList<>(Weather.INSTANCE.getWeatherSpecs());
GBApplication.deviceService().onSendWeather(specs);
}
return true;
@@ -69,7 +69,7 @@ class AppMessageHandlerHealthify extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -146,7 +146,7 @@ class AppMessageHandlerM7S extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -55,7 +55,7 @@ class AppMessageHandlerMarioTime extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -160,7 +160,7 @@ class AppMessageHandlerObsidian extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -28,6 +28,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
class AppMessageHandlerPebStyle extends AppMessageHandler {
@@ -92,11 +93,11 @@ class AppMessageHandlerPebStyle extends AppMessageHandler {
//WEATHER
WeatherSpec weather = Weather.getInstance().getWeatherSpec();
WeatherSpec weather = Weather.INSTANCE.getWeatherSpec();
if (weather != null) {
//comment the same key in the general section above!
pairs.add(new Pair<>(KEY_LOCATION_SERVICE, (Object) 0)); //0 auto, 1 manual
pairs.add(new Pair<>(KEY_WEATHER_CODE, (Object) Weather.mapToYahooCondition(weather.currentConditionCode)));
pairs.add(new Pair<>(KEY_WEATHER_CODE, (Object) WeatherMapper.INSTANCE.mapToYahooCondition(weather.currentConditionCode)));
pairs.add(new Pair<>(KEY_WEATHER_TEMP, (Object) (weather.currentTemp - 273)));
}
@@ -130,7 +130,7 @@ class AppMessageHandlerRealWeather extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -123,7 +123,7 @@ private int getConditionForConditionCode(int conditionCode) {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -78,7 +78,7 @@ class AppMessageHandlerSquare extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -155,7 +155,7 @@ class AppMessageHandlerTimeStylePebble extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -96,7 +96,7 @@ class AppMessageHandlerTrekVolle extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -228,7 +228,7 @@ class AppMessageHandlerYWeather extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -83,7 +83,7 @@ class AppMessageHandlerZalewszczak extends AppMessageHandler {
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
@@ -66,7 +66,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec.Action;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleNotification;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
@@ -1183,10 +1183,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.put((byte) 3); // unknown, always 3?
buf.putShort(currentTemp);
buf.put(Weather.mapToPebbleCondition(weatherSpec.currentConditionCode));
buf.put(WeatherMapper.INSTANCE.mapToPebbleCondition(weatherSpec.currentConditionCode));
buf.putShort(todayMax);
buf.putShort(todayMin);
buf.put(Weather.mapToPebbleCondition(tomorrowConditionCode));
buf.put(WeatherMapper.INSTANCE.mapToPebbleCondition(tomorrowConditionCode));
buf.putShort(tomorrowMax);
buf.putShort(tomorrowMin);
buf.putInt(weatherSpec.timestamp);
@@ -144,7 +144,7 @@ public class GBWebClient extends WebViewClient {
private WebResourceResponse mimicOpenWeatherMapResponse(String type, String units) {
if (Weather.getInstance() == null) {
if (Weather.INSTANCE == null) {
LOG.warn("WEBVIEW - Weather instance is null, cannot update weather");
return null;
}
@@ -152,7 +152,7 @@ public class GBWebClient extends WebViewClient {
CurrentPosition currentPosition = new CurrentPosition();
try {
JSONObject resp = Weather.getInstance().createReconstructedOWMWeatherReply();
JSONObject resp = Weather.INSTANCE.createReconstructedOWMWeatherReply();
if ("/data/2.5/weather".equals(type) && resp != null) {
JSONObject main = resp.getJSONObject("main");
@@ -163,8 +163,8 @@ public class GBWebClient extends WebViewClient {
resp.put("cod", 200);
resp.put("coord", coordObject(currentPosition));
resp.put("sys", sysObject(currentPosition));
// } else if ("/data/2.5/forecast".equals(type) && Weather.getInstance().getWeather2().reconstructedOWMForecast != null) { //this is wrong, as we only have daily data. Unfortunately it looks like daily forecasts cannot be reconstructed
// resp = new JSONObject(Weather.getInstance().getWeather2().reconstructedOWMForecast.toString());
// } else if ("/data/2.5/forecast".equals(type) && Weather.INSTANCE.getWeather2().reconstructedOWMForecast != null) { //this is wrong, as we only have daily data. Unfortunately it looks like daily forecasts cannot be reconstructed
// resp = new JSONObject(Weather.INSTANCE.getWeather2().reconstructedOWMForecast.toString());
//
// JSONObject city = resp.getJSONObject("city");
// city.put("coord", coordObject(currentPosition));
@@ -2003,7 +2003,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
} else if (request.has("weatherInfo") || request.has("weatherApp._.config.locations")) {
LOG.info("Got weatherInfo request");
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec != null) {
onSendWeather(weatherSpec);
} else {
@@ -2011,7 +2011,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
} else if (request.has("widgetChanceOfRain._.config.info")) {
LOG.info("Got widgetChanceOfRain request");
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec != null) {
onSendChanceOfRain(weatherSpec);
} else {
@@ -2019,7 +2019,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
} else if (request.has("widgetUV._.config.info")) {
LOG.info("Got widgetUV request");
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
WeatherSpec weatherSpec = Weather.INSTANCE.getWeatherSpec();
if (weatherSpec != null) {
onSendUVIndex(weatherSpec);
} else {
@@ -517,7 +517,7 @@ public class XiaomiWeatherService extends AbstractXiaomiService {
if (!TextUtils.isEmpty(locationKey) && !TextUtils.isEmpty(locationName)) {
LOG.debug("Received request for conditions (location key = {}, name = {})", locationKey, locationName);
final List<WeatherSpec> knownWeathers = Weather.getInstance().getWeatherSpecs();
final List<WeatherSpec> knownWeathers = Weather.INSTANCE.getWeatherSpecs();
for (WeatherSpec spec : knownWeathers) {
if (TextUtils.equals(spec.location, locationName)) {
sendWeatherSpec(spec);
@@ -529,14 +529,14 @@ public class XiaomiWeatherService extends AbstractXiaomiService {
}
}
final WeatherSpec spec = Weather.getInstance().getWeatherSpec();
final WeatherSpec spec = Weather.INSTANCE.getWeatherSpec();
if (spec == null) {
LOG.warn("Not sending weather conditions: active weather spec is null!");
return;
}
sendWeatherSpec(Weather.getInstance().getWeatherSpec());
sendWeatherSpec(Weather.INSTANCE.getWeatherSpec());
}
private static String[] weatherLocationsToStringArray(final Collection<XiaomiProto.WeatherLocation> locations) {
@@ -567,7 +567,7 @@ public class XiaomiWeatherService extends AbstractXiaomiService {
locationsInitialized = true;
// now that the feature flag has been updated, send cached weather
onSendWeather(Weather.getInstance().getWeatherSpecs());
onSendWeather(Weather.INSTANCE.getWeatherSpecs());
return;
}
@@ -614,7 +614,7 @@ public class XiaomiWeatherService extends AbstractXiaomiService {
final Set<XiaomiProto.WeatherLocation> specLocations = new HashSet<>();
for (final WeatherSpec s : Weather.getInstance().getWeatherSpecs()) {
for (final WeatherSpec s : Weather.INSTANCE.getWeatherSpecs()) {
specLocations.add(getWeatherLocationFromSpec(s));
}
@@ -62,7 +62,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
@@ -617,9 +617,9 @@ public class ZeTimeDeviceSupport extends AbstractBTLESingleDeviceSupport {
LOG.warn("We do not have a sane fw version string available, firmware too old/new?");
}
if (newWeather) {
weather[9] = Weather.mapToZeTimeCondition(weatherSpec.currentConditionCode);
weather[9] = WeatherMapper.INSTANCE.mapToZeTimeCondition(weatherSpec.currentConditionCode);
} else {
weather[9] = Weather.mapToZeTimeConditionOld(weatherSpec.currentConditionCode);
weather[9] = WeatherMapper.INSTANCE.mapToZeTimeConditionOld(weatherSpec.currentConditionCode);
}
for (int forecast = 0; forecast < 3; forecast++) {
weather[10 + (forecast * 5)] = 0; // celsius
@@ -627,9 +627,9 @@ public class ZeTimeDeviceSupport extends AbstractBTLESingleDeviceSupport {
weather[12 + (forecast * 5)] = (byte) (weatherSpec.forecasts.get(forecast).minTemp - 273);
weather[13 + (forecast * 5)] = (byte) (weatherSpec.forecasts.get(forecast).maxTemp - 273);
if (newWeather) {
weather[14 + (forecast * 5)] = Weather.mapToZeTimeCondition(weatherSpec.forecasts.get(forecast).conditionCode);
weather[14 + (forecast * 5)] = WeatherMapper.INSTANCE.mapToZeTimeCondition(weatherSpec.forecasts.get(forecast).conditionCode);
} else {
weather[14 + (forecast * 5)] = Weather.mapToZeTimeConditionOld(weatherSpec.forecasts.get(forecast).conditionCode);
weather[14 + (forecast * 5)] = WeatherMapper.INSTANCE.mapToZeTimeConditionOld(weatherSpec.forecasts.get(forecast).conditionCode);
}
}
System.arraycopy(weatherSpec.location.getBytes(StandardCharsets.UTF_8), 0, weather, 25, weatherSpec.location.getBytes(StandardCharsets.UTF_8).length);
@@ -27,7 +27,7 @@ import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
public class ParcelableWeather2 implements Parcelable {
@@ -99,7 +99,7 @@ public class ParcelableWeather2 implements Parcelable {
condition.put("id", forecastConditionCode);
condition.put("main", forecastBundle.getString("weather_condition_text"));
condition.put("description", forecastBundle.getString("weather_condition_text"));
condition.put("icon", Weather.mapToOpenWeatherMapIcon(forecastConditionCode));
condition.put("icon", WeatherMapper.INSTANCE.mapToOpenWeatherMapIcon(forecastConditionCode));
weather.put(condition);
main.put("temp", forecastBundle.getInt("weather_current_temp"));