diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java index 059521941..834108f2a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java @@ -369,6 +369,10 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni today.setFieldByName("relative_humidity", weather.currentHumidity); today.setFieldByName("observed_location_lat", weather.latitude); 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); weatherData.add(today); @@ -380,13 +384,14 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni weatherHourlyForecast.setFieldByName("timestamp", hourly.timestamp); weatherHourlyForecast.setFieldByName("temperature", hourly.temp); 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_speed", Math.round(hourly.windSpeed)); weatherHourlyForecast.setFieldByName("precipitation_probability", hourly.precipProbability); 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("air_quality", 0); // air_quality enum +// weatherHourlyForecast.setFieldByName("air_quality", 0); // TODO: add once Hourly contains this information weatherData.add(weatherHourlyForecast); } } @@ -399,6 +404,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni todayDailyForecast.setFieldByName("condition", weather.currentConditionCode); todayDailyForecast.setFieldByName("precipitation_probability", weather.precipProbability); todayDailyForecast.setFieldByName("day_of_week", weather.timestamp); + if (null != weather.airQuality) { + todayDailyForecast.setFieldByName("air_quality", weather.airQuality.aqi); + } weatherData.add(todayDailyForecast); @@ -413,6 +421,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni weatherDailyForecast.setFieldByName("high_temperature", daily.maxTemp); weatherDailyForecast.setFieldByName("condition", daily.conditionCode); weatherDailyForecast.setFieldByName("precipitation_probability", daily.precipProbability); + if (null != daily.airQuality) { + weatherDailyForecast.setFieldByName("air_quality", daily.airQuality.aqi); + } weatherDailyForecast.setFieldByName("day_of_week", ts); weatherData.add(weatherDailyForecast); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FieldDefinitionFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FieldDefinitionFactory.java index c9635d04e..497d3c637 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FieldDefinitionFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FieldDefinitionFactory.java @@ -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.FieldDefinitionTemperature; 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; public class FieldDefinitionFactory { @@ -41,6 +42,8 @@ public class FieldDefinitionFactory { return new FieldDefinitionLanguage(localNumber, size, baseType, name); case SLEEP_STAGE: return new FieldDefinitionSleepStage(localNumber, size, baseType, name); + case WEATHER_AQI: + return new FieldDefinitionWeatherAqi(localNumber, size, baseType, name); default: return new FieldDefinition(localNumber, size, baseType, name); } @@ -58,5 +61,6 @@ public class FieldDefinitionFactory { WEATHER_CONDITION, LANGUAGE, SLEEP_STAGE, + WEATHER_AQI, } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java index 631e2441e..ce021ccf0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/GlobalFITMessage.java @@ -183,9 +183,9 @@ public class GlobalFITMessage { 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(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(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) )); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java index 1e8d9309b..a1bfc90fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java @@ -7,13 +7,13 @@ import java.util.List; public enum PredefinedLocalMessage { 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, - 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, - new int[]{0, 253, 14, 13, 2, 5, 12} + new int[]{0, 253, 14, 13, 2, 5, 12, 17} ); private final int type; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/fieldDefinitions/FieldDefinitionWeatherAqi.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/fieldDefinitions/FieldDefinitionWeatherAqi.java new file mode 100644 index 000000000..fa25c2170 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/fieldDefinitions/FieldDefinitionWeatherAqi.java @@ -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, + } +}