From 65aa5faec5e69d96915cdb018778fed42349aeae Mon Sep 17 00:00:00 2001 From: "Martin.JM" Date: Sun, 4 Feb 2024 00:59:04 +0100 Subject: [PATCH] Implement Huawei weather icons --- .../devices/huawei/packets/Weather.java | 222 +++++++++++++++++- .../devices/huawei/HuaweiSupportProvider.java | 52 ++++ .../requests/SendWeatherCurrentRequest.java | 2 + .../requests/SendWeatherForecastRequest.java | 6 +- 4 files changed, 270 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Weather.java index 051b44bfc..8b8922fbe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Weather.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/Weather.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; -import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -48,12 +47,217 @@ public class Weather { public boolean moonPhaseSupported = false; } + public enum WeatherIcon { + // Also used for the text on the watch + SUNNY, + CLOUDY, + OVERCAST, + SHOWERS, + THUNDERSTORMS, + THUNDER_AND_HAIL, + SLEET, + LIGHT_RAIN, + RAIN, + HEAVY_RAIN, + RAIN_STORM, + HEAVY_RAIN_STORMS, + SEVERE_RAIN_STORMS, + SNOW_FLURRIES, + LIGHT_SNOW, + SNOW, + HEAVY_SNOW, + SNOWSTORMS, + FOG, + FREEZING_RAIN, + DUST_STORM, + LIGHT_TO_MODERATE_RAIN, + MODERATE_TO_HEAVY_RAIN, + HEAVY_TO_SEVERE_RAIN, + HEAVY_TO_TORRENTIAL_RAIN, + SEVERE_TO_TORRENTIAL_RAIN, + LIGHT_TO_MODERATE_SNOW, + MODERATE_TO_HEAVY_SNOW, + HEAVY_SNOW_TO_BLIZZARD, + DUST, + SAND, + SANDSTORMS, + FREEZING, // misses small/non-moving icon + HOT, // misses small/non-moving icon + COLD, // misses small/non-moving icon + WINDY, + HAZY, + UNKNOWN // Good to have probably + } + + private static byte iconToByte(WeatherIcon weatherIcon) { + switch (weatherIcon) { + case SUNNY: + return 0x00; + case CLOUDY: + return 0x01; + case OVERCAST: + return 0x02; + case SHOWERS: + return 0x03; + case THUNDERSTORMS: + return 0x04; + case THUNDER_AND_HAIL: + return 0x05; + case SLEET: + return 0x06; + case LIGHT_RAIN: + return 0x07; + case RAIN: + return 0x08; + case HEAVY_RAIN: + return 0x09; + case RAIN_STORM: + return 0x0a; + case HEAVY_RAIN_STORMS: + return 0x0b; + case SEVERE_RAIN_STORMS: + return 0x0c; + case SNOW_FLURRIES: + return 0x0d; + case LIGHT_SNOW: + return 0x0e; + case SNOW: + return 0x0f; + case HEAVY_SNOW: + return 0x10; + case SNOWSTORMS: + return 0x11; + case FOG: + return 0x12; + case FREEZING_RAIN: + return 0x13; + case DUST_STORM: + return 0x14; + case LIGHT_TO_MODERATE_RAIN: + return 0x15; + case MODERATE_TO_HEAVY_RAIN: + return 0x16; + case HEAVY_TO_SEVERE_RAIN: + return 0x17; + case HEAVY_TO_TORRENTIAL_RAIN: + return 0x18; + case SEVERE_TO_TORRENTIAL_RAIN: + return 0x19; + case LIGHT_TO_MODERATE_SNOW: + return 0x1a; + case MODERATE_TO_HEAVY_SNOW: + return 0x1b; + case HEAVY_SNOW_TO_BLIZZARD: + return 0x1c; + case DUST: + return 0x1d; + case SAND: + return 0x1e; + case SANDSTORMS: + return 0x1f; + case FREEZING: + return 0x20; + case HOT: + return 0x21; + case COLD: + return 0x22; + case WINDY: + return 0x23; + case HAZY: + return 0x35; + default: + return 0x63; // Any higher and the current weather breaks + } + } + + private static WeatherIcon byteToIcon(byte weatherIcon) { + switch (weatherIcon) { + case 0x00: + return WeatherIcon.SUNNY; + case 0x01: + return WeatherIcon.CLOUDY; + case 0x02: + return WeatherIcon.OVERCAST; + case 0x03: + return WeatherIcon.SHOWERS; + case 0x04: + return WeatherIcon.THUNDERSTORMS; + case 0x05: + return WeatherIcon.THUNDER_AND_HAIL; + case 0x06: + return WeatherIcon.SLEET; + case 0x07: + return WeatherIcon.LIGHT_RAIN; + case 0x08: + return WeatherIcon.RAIN; + case 0x09: + return WeatherIcon.HEAVY_RAIN; + case 0x0a: + return WeatherIcon.RAIN_STORM; + case 0x0b: + return WeatherIcon.HEAVY_RAIN_STORMS; + case 0x0c: + return WeatherIcon.SEVERE_RAIN_STORMS; + case 0x0d: + return WeatherIcon.SNOW_FLURRIES; + case 0x0e: + return WeatherIcon.LIGHT_SNOW; + case 0x0f: + return WeatherIcon.SNOW; + case 0x10: + return WeatherIcon.HEAVY_SNOW; + case 0x11: + return WeatherIcon.SNOWSTORMS; + case 0x12: + return WeatherIcon.FOG; + case 0x13: + return WeatherIcon.FREEZING_RAIN; + case 0x14: + return WeatherIcon.DUST_STORM; + case 0x15: + return WeatherIcon.LIGHT_TO_MODERATE_RAIN; + case 0x16: + return WeatherIcon.MODERATE_TO_HEAVY_RAIN; + case 0x17: + return WeatherIcon.HEAVY_TO_SEVERE_RAIN; + case 0x18: + return WeatherIcon.HEAVY_TO_TORRENTIAL_RAIN; + case 0x19: + return WeatherIcon.SEVERE_TO_TORRENTIAL_RAIN; + case 0x1a: + return WeatherIcon.LIGHT_TO_MODERATE_SNOW; + case 0x1b: + return WeatherIcon.MODERATE_TO_HEAVY_SNOW; + case 0x1c: + return WeatherIcon.HEAVY_SNOW_TO_BLIZZARD; + case 0x1d: + return WeatherIcon.DUST; + case 0x1e: + return WeatherIcon.SAND; + case 0x1f: + return WeatherIcon.SANDSTORMS; + case 0x20: + return WeatherIcon.FREEZING; + case 0x21: + return WeatherIcon.HOT; + case 0x22: + return WeatherIcon.COLD; + case 0x23: + return WeatherIcon.WINDY; + case 0x35: + return WeatherIcon.HAZY; + default: + return WeatherIcon.UNKNOWN; + } + } + public static class CurrentWeatherRequest extends HuaweiPacket { public static final byte id = 0x01; public CurrentWeatherRequest( ParamsProvider paramsProvider, Settings settings, + WeatherIcon icon, Byte windDirection, Byte windSpeed, Byte lowestTemperature, @@ -74,8 +278,8 @@ public class Weather { HuaweiTLV tlv81 = new HuaweiTLV(); - if (settings.weatherIconSupported) { - tlv81.put(0x02, (byte) 0x01); + if (icon != null && settings.weatherIconSupported) { + tlv81.put(0x02, iconToByte(icon)); } if (settings.windSupported) { @@ -235,7 +439,7 @@ public class Weather { public static class TimeData { public int timestamp; - public byte icon; + public WeatherIcon icon; public byte temperature; @Override @@ -252,7 +456,7 @@ public class Weather { public static class DayData { public int timestamp; - public byte icon; + public WeatherIcon icon; public byte highTemperature; public byte lowTemperature; public int sunriseTime; @@ -297,7 +501,7 @@ public class Weather { // TODO: NULLs? timeDataTlv.put(0x82, new HuaweiTLV() .put(0x03, timeData.timestamp) - .put(0x04, timeData.icon) + .put(0x04, iconToByte(timeData.icon)) .put(0x05, timeData.temperature) ); } @@ -311,7 +515,7 @@ public class Weather { // TODO: NULLs? dayDataTlv.put(0x91, new HuaweiTLV() .put(0x12, dayData.timestamp) - .put(0x13, dayData.icon) + .put(0x13, iconToByte(dayData.icon)) .put(0x14, dayData.highTemperature) .put(0x15, dayData.lowTemperature) .put(0x16, dayData.sunriseTime) @@ -346,7 +550,7 @@ public class Weather { for (HuaweiTLV timeTlv : this.tlv.getObject(0x81).getObjects(0x82)) { TimeData timeData = new TimeData(); timeData.timestamp = timeTlv.getInteger(0x03); - timeData.icon = timeTlv.getByte(0x04); + timeData.icon = byteToIcon(timeTlv.getByte(0x04)); timeData.temperature = timeTlv.getByte(0x05); timeDataList.add(timeData); } @@ -355,7 +559,7 @@ public class Weather { for (HuaweiTLV dayTlv : this.tlv.getObject(0x90).getObjects(0x91)) { DayData dayData = new DayData(); dayData.timestamp = dayTlv.getInteger(0x12); - dayData.icon = dayTlv.getByte(0x13); + dayData.icon = byteToIcon(dayTlv.getByte(0x13)); dayData.highTemperature = dayTlv.getByte(0x14); dayData.lowTemperature = dayTlv.getByte(0x15); dayData.sunriseTime = dayTlv.getInteger(0x16); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java index 61a49e3c7..01befd429 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java @@ -1650,6 +1650,58 @@ public class HuaweiSupportProvider { } } + public Weather.WeatherIcon openWeatherMapConditionCodeToHuaweiIcon(int conditionCode) { + // More exact first, groups after + switch (conditionCode) { + case 500: + return Weather.WeatherIcon.LIGHT_RAIN; + case 501: + return Weather.WeatherIcon.RAIN; + case 502: + return Weather.WeatherIcon.HEAVY_RAIN; + case 503: + return Weather.WeatherIcon.RAIN_STORM; + case 504: + return Weather.WeatherIcon.SEVERE_RAIN_STORMS; + case 511: + return Weather.WeatherIcon.FREEZING_RAIN; + case 600: + return Weather.WeatherIcon.LIGHT_SNOW; + case 601: + return Weather.WeatherIcon.SNOW; + case 602: + return Weather.WeatherIcon.HEAVY_SNOW; + case 611: + return Weather.WeatherIcon.SLEET; + case 701: + case 741: + return Weather.WeatherIcon.FOG; + case 721: + return Weather.WeatherIcon.HAZY; + case 751: + return Weather.WeatherIcon.SAND; + case 761: + return Weather.WeatherIcon.DUST; + case 800: + return Weather.WeatherIcon.SUNNY; + case 801: + case 802: + return Weather.WeatherIcon.CLOUDY; + case 803: + case 804: + return Weather.WeatherIcon.OVERCAST; + } + if (conditionCode >= 200 && conditionCode < 300) + return Weather.WeatherIcon.THUNDERSTORMS; + if (conditionCode >= 300 && conditionCode < 400) + return Weather.WeatherIcon.LIGHT_RAIN; + if (conditionCode >= 500 && conditionCode < 600) + return Weather.WeatherIcon.RAIN; + if (conditionCode >= 600 && conditionCode < 700) + return Weather.WeatherIcon.SNOW; + return Weather.WeatherIcon.UNKNOWN; + } + public void onSendWeather(WeatherSpec weatherSpec) { if (weatherSettings != null && weatherSettings.weatherSupported) { try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherCurrentRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherCurrentRequest.java index 235ce4723..ee75abe52 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherCurrentRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherCurrentRequest.java @@ -22,6 +22,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; public class SendWeatherCurrentRequest extends Request { Weather.Settings settings; @@ -48,6 +49,7 @@ public class SendWeatherCurrentRequest extends Request { return new Weather.CurrentWeatherRequest( this.paramsProvider, settings, + supportProvider.openWeatherMapConditionCodeToHuaweiIcon(weatherSpec.currentConditionCode), (byte) weatherSpec.windDirection, (byte) weatherSpec.windSpeedAsBeaufort(), (byte) (weatherSpec.todayMinTemp - 273), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherForecastRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherForecastRequest.java index 93dacca79..90fc46a27 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherForecastRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendWeatherForecastRequest.java @@ -49,7 +49,7 @@ public class SendWeatherForecastRequest extends Request { WeatherSpec.Hourly hourly = weatherSpec.hourly.get(i); WeatherForecastData.TimeData timeData = new WeatherForecastData.TimeData(); timeData.timestamp = hourly.timestamp; - timeData.icon = 1; // TODO: hourly.conditionCode conversion + timeData.icon = supportProvider.openWeatherMapConditionCodeToHuaweiIcon(hourly.conditionCode); // TODO: hourly.conditionCode conversion timeData.temperature = (byte) (hourly.temp - 273); timeDataArrayList.add(timeData); } @@ -57,7 +57,7 @@ public class SendWeatherForecastRequest extends Request { // Add today as well WeatherForecastData.DayData today = new WeatherForecastData.DayData(); today.timestamp = weatherSpec.sunRise; - today.icon = 1; // TODO + today.icon = supportProvider.openWeatherMapConditionCodeToHuaweiIcon(weatherSpec.currentConditionCode); today.highTemperature = (byte) (weatherSpec.todayMaxTemp - 273); today.lowTemperature = (byte) (weatherSpec.todayMinTemp - 273); today.sunriseTime = weatherSpec.sunRise; @@ -71,7 +71,7 @@ public class SendWeatherForecastRequest extends Request { WeatherSpec.Daily daily = weatherSpec.forecasts.get(i); WeatherForecastData.DayData dayData = new WeatherForecastData.DayData(); dayData.timestamp = daily.sunRise; - dayData.icon = 1; // TODO: daily.conditionCode conversion + dayData.icon = supportProvider.openWeatherMapConditionCodeToHuaweiIcon(daily.conditionCode); dayData.highTemperature = (byte) (daily.maxTemp - 273); dayData.lowTemperature = (byte) (daily.minTemp - 273); dayData.sunriseTime = daily.sunRise;