1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-01 06:22:55 +01:00

Garmin: improve weather information

Add Aqi Field Definition and field to today weather and daily forecast, as both are available in WeatherSpec.
Add Feels like temperature to hourly forecast but populate with the forecasted temperature as the field is not available in Weatherspec for hourly.
Use temperature Field Definition for dew point and add it to today's weather.

Fields dew point and air quality could have been removed from the hourly weather definition but are kept in to test compatibility of these changes with watches.
This commit is contained in:
Daniele Gobbetti 2024-05-06 11:32:16 +02:00
parent ebe371de3c
commit 2a9a01e40b
5 changed files with 78 additions and 7 deletions

View File

@ -369,6 +369,10 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
today.setFieldByName("relative_humidity", weather.currentHumidity); today.setFieldByName("relative_humidity", weather.currentHumidity);
today.setFieldByName("observed_location_lat", weather.latitude); today.setFieldByName("observed_location_lat", weather.latitude);
today.setFieldByName("observed_location_long", weather.longitude); today.setFieldByName("observed_location_long", weather.longitude);
today.setFieldByName("dew_point", weather.dewPoint);
if (null != weather.airQuality) {
today.setFieldByName("air_quality", weather.airQuality.aqi);
}
today.setFieldByName("location", weather.location); today.setFieldByName("location", weather.location);
weatherData.add(today); weatherData.add(today);
@ -380,13 +384,14 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
weatherHourlyForecast.setFieldByName("timestamp", hourly.timestamp); weatherHourlyForecast.setFieldByName("timestamp", hourly.timestamp);
weatherHourlyForecast.setFieldByName("temperature", hourly.temp); weatherHourlyForecast.setFieldByName("temperature", hourly.temp);
weatherHourlyForecast.setFieldByName("condition", hourly.conditionCode); weatherHourlyForecast.setFieldByName("condition", hourly.conditionCode);
weatherHourlyForecast.setFieldByName("temperature_feels_like", hourly.temp); //TODO: switch to actual feels like field once Hourly contains this information
weatherHourlyForecast.setFieldByName("wind_direction", hourly.windDirection); weatherHourlyForecast.setFieldByName("wind_direction", hourly.windDirection);
weatherHourlyForecast.setFieldByName("wind_speed", Math.round(hourly.windSpeed)); weatherHourlyForecast.setFieldByName("wind_speed", Math.round(hourly.windSpeed));
weatherHourlyForecast.setFieldByName("precipitation_probability", hourly.precipProbability); weatherHourlyForecast.setFieldByName("precipitation_probability", hourly.precipProbability);
weatherHourlyForecast.setFieldByName("relative_humidity", hourly.humidity); weatherHourlyForecast.setFieldByName("relative_humidity", hourly.humidity);
// weatherHourlyForecast.setFieldByName("dew_point", 0); // dew_point sint8 // weatherHourlyForecast.setFieldByName("dew_point", 0); // TODO: add once Hourly contains this information
weatherHourlyForecast.setFieldByName("uv_index", hourly.uvIndex); weatherHourlyForecast.setFieldByName("uv_index", hourly.uvIndex);
// weatherHourlyForecast.setFieldByName("air_quality", 0); // air_quality enum // weatherHourlyForecast.setFieldByName("air_quality", 0); // TODO: add once Hourly contains this information
weatherData.add(weatherHourlyForecast); weatherData.add(weatherHourlyForecast);
} }
} }
@ -399,6 +404,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
todayDailyForecast.setFieldByName("condition", weather.currentConditionCode); todayDailyForecast.setFieldByName("condition", weather.currentConditionCode);
todayDailyForecast.setFieldByName("precipitation_probability", weather.precipProbability); todayDailyForecast.setFieldByName("precipitation_probability", weather.precipProbability);
todayDailyForecast.setFieldByName("day_of_week", weather.timestamp); todayDailyForecast.setFieldByName("day_of_week", weather.timestamp);
if (null != weather.airQuality) {
todayDailyForecast.setFieldByName("air_quality", weather.airQuality.aqi);
}
weatherData.add(todayDailyForecast); weatherData.add(todayDailyForecast);
@ -413,6 +421,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
weatherDailyForecast.setFieldByName("high_temperature", daily.maxTemp); weatherDailyForecast.setFieldByName("high_temperature", daily.maxTemp);
weatherDailyForecast.setFieldByName("condition", daily.conditionCode); weatherDailyForecast.setFieldByName("condition", daily.conditionCode);
weatherDailyForecast.setFieldByName("precipitation_probability", daily.precipProbability); weatherDailyForecast.setFieldByName("precipitation_probability", daily.precipProbability);
if (null != daily.airQuality) {
weatherDailyForecast.setFieldByName("air_quality", daily.airQuality.aqi);
}
weatherDailyForecast.setFieldByName("day_of_week", ts); weatherDailyForecast.setFieldByName("day_of_week", ts);
weatherData.add(weatherDailyForecast); weatherData.add(weatherDailyForecast);
} }

View File

@ -11,6 +11,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefi
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTemperature; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTemperature;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTimestamp; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTimestamp;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherAqi;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherCondition; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherCondition;
public class FieldDefinitionFactory { public class FieldDefinitionFactory {
@ -41,6 +42,8 @@ public class FieldDefinitionFactory {
return new FieldDefinitionLanguage(localNumber, size, baseType, name); return new FieldDefinitionLanguage(localNumber, size, baseType, name);
case SLEEP_STAGE: case SLEEP_STAGE:
return new FieldDefinitionSleepStage(localNumber, size, baseType, name); return new FieldDefinitionSleepStage(localNumber, size, baseType, name);
case WEATHER_AQI:
return new FieldDefinitionWeatherAqi(localNumber, size, baseType, name);
default: default:
return new FieldDefinition(localNumber, size, baseType, name); return new FieldDefinition(localNumber, size, baseType, name);
} }
@ -58,5 +61,6 @@ public class FieldDefinitionFactory {
WEATHER_CONDITION, WEATHER_CONDITION,
LANGUAGE, LANGUAGE,
SLEEP_STAGE, SLEEP_STAGE,
WEATHER_AQI,
} }
} }

View File

@ -183,9 +183,9 @@ public class GlobalFITMessage {
new FieldDefinitionPrimitive(12, BaseType.ENUM, "day_of_week", FieldDefinitionFactory.FIELD.DAY_OF_WEEK), new FieldDefinitionPrimitive(12, BaseType.ENUM, "day_of_week", FieldDefinitionFactory.FIELD.DAY_OF_WEEK),
new FieldDefinitionPrimitive(13, BaseType.SINT8, "high_temperature", FieldDefinitionFactory.FIELD.TEMPERATURE), new FieldDefinitionPrimitive(13, BaseType.SINT8, "high_temperature", FieldDefinitionFactory.FIELD.TEMPERATURE),
new FieldDefinitionPrimitive(14, BaseType.SINT8, "low_temperature", FieldDefinitionFactory.FIELD.TEMPERATURE), new FieldDefinitionPrimitive(14, BaseType.SINT8, "low_temperature", FieldDefinitionFactory.FIELD.TEMPERATURE),
new FieldDefinitionPrimitive(15, BaseType.SINT8, "dew_point"), new FieldDefinitionPrimitive(15, BaseType.SINT8, "dew_point", FieldDefinitionFactory.FIELD.TEMPERATURE),
new FieldDefinitionPrimitive(16, BaseType.FLOAT32, "uv_index"), new FieldDefinitionPrimitive(16, BaseType.FLOAT32, "uv_index"),
new FieldDefinitionPrimitive(17, BaseType.ENUM, "air_quality"), new FieldDefinitionPrimitive(17, BaseType.ENUM, "air_quality", FieldDefinitionFactory.FIELD.WEATHER_AQI),
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP) new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
)); ));

View File

@ -7,13 +7,13 @@ import java.util.List;
public enum PredefinedLocalMessage { public enum PredefinedLocalMessage {
TODAY_WEATHER_CONDITIONS(6, GlobalFITMessage.WEATHER, TODAY_WEATHER_CONDITIONS(6, GlobalFITMessage.WEATHER,
new int[]{0, 253, 9, 1, 14, 13, 2, 3, 5, 4, 6, 7, 10, 11, 8} new int[]{0, 253, 9, 1, 14, 13, 2, 3, 5, 4, 6, 7, 10, 11, 17, 15, 8}
), ),
HOURLY_WEATHER_FORECAST(9, GlobalFITMessage.WEATHER, HOURLY_WEATHER_FORECAST(9, GlobalFITMessage.WEATHER,
new int[]{0, 253, 1, 2, 3, 4, 5, 7, 15, 16, 17} new int[]{0, 253, 1, 2, 3, 4, 5, 6, 7, 15, 16, 17}
), ),
DAILY_WEATHER_FORECAST(10, GlobalFITMessage.WEATHER, DAILY_WEATHER_FORECAST(10, GlobalFITMessage.WEATHER,
new int[]{0, 253, 14, 13, 2, 5, 12} new int[]{0, 253, 14, 13, 2, 5, 12, 17}
); );
private final int type; private final int type;

View File

@ -0,0 +1,56 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
public class FieldDefinitionWeatherAqi extends FieldDefinition {
public FieldDefinitionWeatherAqi(int localNumber, int size, BaseType baseType, String name) {
super(localNumber, size, baseType, name, 1, 0);
}
@Override
public Object decode(ByteBuffer byteBuffer) {
int idx = (int) baseType.decode(byteBuffer, scale, offset);
return AQI_LEVELS.values()[idx];
}
@Override
public void encode(ByteBuffer byteBuffer, Object o) {
if (o instanceof AQI_LEVELS) {
baseType.encode(byteBuffer, ((AQI_LEVELS) o).ordinal(), scale, offset);
return;
}
baseType.encode(byteBuffer, aqiAbsoluteValueToIndex((int) o), scale, offset);
}
private int aqiAbsoluteValueToIndex(int rawValue) { //see https://github.com/breezy-weather/breezy-weather/blob/main/app/src/main/java/org/breezyweather/domain/weather/index/PollutantIndex.kt#L38
if (rawValue == -1) {
return rawValue; //invalid
}
if (rawValue < 20) {
return AQI_LEVELS.GOOD.ordinal();
} else if (rawValue < 50) {
return AQI_LEVELS.MODERATE.ordinal();
} else if (rawValue < 100) {
return AQI_LEVELS.UNHEALTHY_SENSITIVE.ordinal();
} else if (rawValue < 150) {
return AQI_LEVELS.UNHEALTHY.ordinal();
} else if (rawValue < 250) {
return AQI_LEVELS.VERY_UNHEALTHY.ordinal();
} else {
return AQI_LEVELS.HAZARDOUS.ordinal();
}
}
public enum AQI_LEVELS {
GOOD,
MODERATE,
UNHEALTHY_SENSITIVE,
UNHEALTHY,
VERY_UNHEALTHY,
HAZARDOUS,
}
}