diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFConstants.java index c31610e16..d7943c2a9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFConstants.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2022 Taavi Eomäe, Stephan Lachnit, ITCactus +/* Copyright (C) 2020-2021 Taavi Eomäe This file is part of Gadgetbridge. @@ -34,14 +34,8 @@ public class PineTimeJFConstants { public static final UUID UUID_CHARACTERISTICS_MUSIC_REPEAT = UUID.fromString("0000000b-78fc-48fe-8e23-433b3a1942d0"); public static final UUID UUID_CHARACTERISTICS_MUSIC_SHUFFLE = UUID.fromString("0000000c-78fc-48fe-8e23-433b3a1942d0"); - public static final UUID UUID_SERVICE_NAVIGATION = UUID.fromString("00010000-78fc-48fe-8e23-433b3a1942d0"); - public static final UUID UUID_CHARACTERISTIC_ALERT_NOTIFICATION_EVENT = UUID.fromString("00020001-78fc-48fe-8e23-433b3a1942d0"); - public static final UUID UUID_SERVICE_WEATHER = UUID.fromString("00040000-78fc-48fe-8e23-433b3a1942d0"); - public static final UUID UUID_CHARACTERISTIC_WEATHER_DATA = UUID.fromString("00040001-78fc-48fe-8e23-433b3a1942d0"); - public static final UUID UUID_CHARACTERISTIC_WEATHER_CONTROL = UUID.fromString("00040002-78fc-48fe-8e23-433b3a1942d0"); - // since 1.7. https://github.com/InfiniTimeOrg/InfiniTime/blob/develop/doc/MotionService.md public static final UUID UUID_SERVICE_MOTION = UUID.fromString("00030000-78fc-48fe-8e23-433b3a1942d0"); public static final UUID UUID_CHARACTERISTIC_MOTION_STEP_COUNT = UUID.fromString("00030001-78fc-48fe-8e23-433b3a1942d0"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java index 000d076ca..57f66a2fe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/PineTimeJFCoordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2022 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2016-2021 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, José Rebelo, Taavi Eomäe This file is part of Gadgetbridge. @@ -126,7 +126,7 @@ public class PineTimeJFCoordinator extends AbstractBLEDeviceCoordinator { @Override public boolean supportsWeather() { - return true; + return false; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/weather/WeatherData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/weather/WeatherData.java deleted file mode 100644 index 84915a2ff..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pinetime/weather/WeatherData.java +++ /dev/null @@ -1,927 +0,0 @@ -/* Copyright (C) 2021-2022 TaaviE - - This file is part of Gadgetbridge. - - Gadgetbridge is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Gadgetbridge is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.devices.pinetime.weather; - -/** - * Implemented based on and other material: - * https://en.wikipedia.org/wiki/METAR - * https://www.weather.gov/jetstream/obscurationtypes - * http://www.faraim.org/aim/aim-4-03-14-493.html - */ -public class WeatherData { - /** - * OpenWeather's condition codes are a bit annoying, - * they've mixed precipitation with other weather events. - *

- * Even if they're absolutely not mutually exclusive. - *

- * So, this function only returns PrecipitationType - */ - public static final PrecipitationType mapOpenWeatherConditionToPineTimePrecipitation(int openWeatherMapCondition) { - switch (openWeatherMapCondition) { - // Group 2xx: Thunderstorm - case 200: // Thunderstorm with light rain - case 201: // Thunderstorm with rain - case 202: // Thunderstorm with heavy rain - case 210: // Light thunderstorm - case 211: // Thunderstorm - case 230: // Thunderstorm with light drizzle - case 231: // Thunderstorm with drizzle - case 232: // Thunderstorm with heavy drizzle - case 212: // Heavy thunderstorm - case 221: // Ragged thunderstorm - return PrecipitationType.Rain; - // Group 3xx: Drizzle - case 300: // Light intensity drizzle - case 301: // Drizzle - case 302: // Heavy intensity drizzle - case 310: // Light intensity drizzle rain - case 311: // Drizzle rain - case 312: // Heavy intensity drizzle rain - case 313: // Shower rain and drizzle - case 314: // Heavy shower rain and drizzle - case 321: // Shower drizzle - return PrecipitationType.Drizzle; - // Group 5xx: Rain - case 500: // Light rain - case 501: // Moderate rain - case 502: // Heavy intensity rain - case 503: // Very heavy rain - case 504: // Extreme rain - return PrecipitationType.Rain; - case 511: // Freezing rain - return PrecipitationType.FreezingRain; - case 520: // Light intensity shower rain - case 521: // Shower rain - case 522: // Heavy intensity shower rain - case 531: // Ragged shower rain - return PrecipitationType.Rain; - // Group 6xx: Snow - case 600: // Light snow - case 601: // Snow - case 620: // Light shower snow - case 602: // Heavy snow - return PrecipitationType.Snow; - case 611: // Sleet - case 612: // Shower sleet - return PrecipitationType.Sleet; - case 621: // Shower snow - case 622: // Heavy shower snow - return PrecipitationType.Snow; - case 615: // Light rain and snow - case 616: // Rain and snow - return PrecipitationType.Sleet; - // Group 7xx: Atmosphere - case 701: // Mist - case 711: // Smoke - case 721: // Haze - case 731: // Sandcase/dust whirls - case 741: // Fog - case 751: // Sand - case 761: // Dust - case 762: // Volcanic ash - return PrecipitationType.Ash; - case 771: // Squalls - case 781: // Tornado - case 900: // Tornado - // Group 800: Clear - case 800: // Clear sky - return PrecipitationType.None; - // Group 80x: Clouds - case 801: // Few clouds - case 802: // Scattered clouds - case 803: // Broken clouds - case 804: // Overcast clouds - // Group 90x: Extreme - case 901: // Tropical storm - case 903: // Cold - case 904: // Hot - case 905: // Windy - case 906: // Hail - return PrecipitationType.Hail; - // Group 9xx: Additional - case 951: // Calm - case 952: // Light breeze - case 953: // Gentle breeze - case 954: // Moderate breeze - case 955: // Fresh breeze - case 956: // Strong breeze - case 957: // High windcase near gale - case 958: // Gale - case 959: // Severe gale - case 960: // Storm - case 961: // Violent storm - case 902: // Hurricane - case 962: // Hurricane - default: - return PrecipitationType.Length; - } - } - - ; - - /** - * OpenWeather's condition codes are a bit annoying, - * they've mixed obscuration with other weather events. - * - *

- * Even if they're absolutely not mutually exclusive. - *

- * So, this function only returns ObscurationType - */ - public static final ObscurationType mapOpenWeatherConditionToPineTimeObscuration(int openWeatherMapCondition) { - switch (openWeatherMapCondition) { - // Group 2xx: Thunderstorm - case 200: // Thunderstorm with light rain - case 201: // Thunderstorm with rain - case 202: // Thunderstorm with heavy rain - case 210: // Light thunderstorm - case 211: // Thunderstorm - case 230: // Thunderstorm with light drizzle - case 231: // Thunderstorm with drizzle - case 232: // Thunderstorm with heavy drizzle - case 212: // Heavy thunderstorm - case 221: // Ragged thunderstorm - return ObscurationType.Precipitation; - // Group 3xx: Drizzle - case 300: // Light intensity drizzle - case 301: // Drizzle - case 302: // Heavy intensity drizzle - case 310: // Light intensity drizzle rain - case 311: // Drizzle rain - case 312: // Heavy intensity drizzle rain - case 313: // Shower rain and drizzle - case 314: // Heavy shower rain and drizzle - case 321: // Shower drizzle - return ObscurationType.Precipitation; - // Group 5xx: Rain - case 500: // Light rain - case 501: // Moderate rain - case 502: // Heavy intensity rain - case 503: // Very heavy rain - case 504: // Extreme rain - case 511: // Freezing rain - case 520: // Light intensity shower rain - case 521: // Shower rain - case 522: // Heavy intensity shower rain - case 531: // Ragged shower rain - return ObscurationType.Precipitation; - // Group 6xx: Snow - case 600: // Light snow - case 601: // Snow - case 620: // Light shower snow - case 602: // Heavy snow - case 611: // Sleet - case 612: // Shower sleet - case 621: // Shower snow - case 622: // Heavy shower snow - case 615: // Light rain and snow - case 616: // Rain and snow - return ObscurationType.Precipitation; - // Group 7xx: Atmosphere - case 701: // Mist - return ObscurationType.Mist; - case 711: // Smoke - return ObscurationType.Smoke; - case 721: // Haze - return ObscurationType.Haze; - case 731: // Sandcase/dust whirls - return ObscurationType.Sand; - case 741: // Fog - return ObscurationType.Fog; - case 751: // Sand - return ObscurationType.Sand; - case 761: // Dust - return ObscurationType.Dust; - case 762: // Volcanic ash - return ObscurationType.Ash; - case 771: // Squalls - return ObscurationType.Length; - case 781: // Tornado - case 900: // Tornado - return ObscurationType.Precipitation; - // Group 800: Clear - case 800: // Clear sky - return ObscurationType.Length; - // Group 80x: Clouds - case 801: // Few clouds - case 802: // Scattered clouds - case 803: // Broken clouds - case 804: // Overcast clouds - return ObscurationType.Length; - // Group 90x: Extreme - case 901: // Tropical storm - return ObscurationType.Precipitation; - case 903: // Cold - case 904: // Hot - return ObscurationType.Length; - case 905: // Windy - case 906: // Hail - return ObscurationType.Precipitation; - // Group 9xx: Additional - case 951: // Calm - case 952: // Light breeze - case 953: // Gentle breeze - case 954: // Moderate breeze - case 955: // Fresh breeze - case 956: // Strong breeze - case 957: // High windcase near gale - case 958: // Gale - case 959: // Severe gale - return ObscurationType.Length; - case 960: // Storm - case 961: // Violent storm - return ObscurationType.Precipitation; - case 902: // Hurricane - return ObscurationType.Precipitation; - case 962: // Hurricane - return ObscurationType.Precipitation; - default: - return ObscurationType.Length; - } - } - - ; - - /** - * OpenWeather's condition codes are a bit annoying, - * they've mixed precipitation with other weather events. - *

- * Even if they're absolutely not mutually exclusive. - * - *

- * So, this function only returns SpecialType - */ - public static final SpecialType mapOpenWeatherConditionToPineTimeSpecial(int openWeatherMapCondition) { - switch (openWeatherMapCondition) { - // Group 2xx: Thunderstorm - case 200: // Thunderstorm with light rain - case 201: // Thunderstorm with rain - case 202: // Thunderstorm with heavy rain - case 210: // Light thunderstorm - case 211: // Thunderstorm - case 230: // Thunderstorm with light drizzle - case 231: // Thunderstorm with drizzle - case 232: // Thunderstorm with heavy drizzle - case 212: // Heavy thunderstorm - case 221: // Ragged thunderstorm - // Group 3xx: Drizzle - case 300: // Light intensity drizzle - case 301: // Drizzle - case 302: // Heavy intensity drizzle - case 310: // Light intensity drizzle rain - case 311: // Drizzle rain - case 312: // Heavy intensity drizzle rain - case 313: // Shower rain and drizzle - case 314: // Heavy shower rain and drizzle - case 321: // Shower drizzle - // Group 5xx: Rain - case 500: // Light rain - case 501: // Moderate rain - case 502: // Heavy intensity rain - case 503: // Very heavy rain - case 504: // Extreme rain - case 511: // Freezing rain - case 520: // Light intensity shower rain - case 521: // Shower rain - case 522: // Heavy intensity shower rain - case 531: // Ragged shower rain - // Group 6xx: Snow - case 600: // Light snow - case 601: // Snow - case 620: // Light shower snow - case 602: // Heavy snow - case 611: // Sleet - case 612: // Shower sleet - case 621: // Shower snow - case 622: // Heavy shower snow - case 615: // Light rain and snow - case 616: // Rain and snow - // Group 7xx: Atmosphere - case 701: // Mist - case 711: // Smoke - case 721: // Haze - case 731: // Sandcase/dust whirls - case 741: // Fog - case 751: // Sand - case 761: // Dust - case 762: // Volcanic ash - return SpecialType.Length; - case 771: // Squalls - return SpecialType.Squall; - case 781: // Tornado - case 900: // Tornado - // Group 800: Clear - case 800: // Clear sky - // Group 80x: Clouds - case 801: // Few clouds - case 802: // Scattered clouds - case 803: // Broken clouds - case 804: // Overcast clouds - // Group 90x: Extreme - case 901: // Tropical storm - case 903: // Cold - return SpecialType.Length; - case 904: // Hot - return SpecialType.Fire; - case 905: // Windy - case 906: // Hail - // Group 9xx: Additional - case 951: // Calm - case 952: // Light breeze - case 953: // Gentle breeze - case 954: // Moderate breeze - case 955: // Fresh breeze - case 956: // Strong breeze - case 957: // High windcase near gale - case 958: // Gale - case 959: // Severe gale - case 960: // Storm - case 961: // Violent storm - case 902: // Hurricane - case 962: // Hurricane - default: - return SpecialType.Length; - } - } - - ; - - /** - * OpenWeather's condition codes are a bit annoying, - * they've mixed precipitation with other weather events. - *

- * Even if they're absolutely not mutually exclusive. - * - *

- * So, this function only returns cloudyness 0-100% - */ - public static final int mapOpenWeatherConditionToCloudCover(int openWeatherMapCondition) { - switch (openWeatherMapCondition) { - // Group 2xx: Thunderstorm - case 200: // Thunderstorm with light rain - case 201: // Thunderstorm with rain - case 202: // Thunderstorm with heavy rain - case 210: // Light thunderstorm - case 211: // Thunderstorm - case 230: // Thunderstorm with light drizzle - case 231: // Thunderstorm with drizzle - case 232: // Thunderstorm with heavy drizzle - case 212: // Heavy thunderstorm - case 221: // Ragged thunderstorm - return 100; - // Group 3xx: Drizzle - case 300: // Light intensity drizzle - case 301: // Drizzle - case 302: // Heavy intensity drizzle - case 310: // Light intensity drizzle rain - case 311: // Drizzle rain - case 312: // Heavy intensity drizzle rain - case 313: // Shower rain and drizzle - case 314: // Heavy shower rain and drizzle - case 321: // Shower drizzle - return 75; - // Group 5xx: Rain - case 500: // Light rain - case 501: // Moderate rain - case 502: // Heavy intensity rain - case 503: // Very heavy rain - case 504: // Extreme rain - case 511: // Freezing rain - case 520: // Light intensity shower rain - case 521: // Shower rain - case 522: // Heavy intensity shower rain - case 531: // Ragged shower rain - return 75; - // Group 6xx: Snow - case 600: // Light snow - case 601: // Snow - case 620: // Light shower snow - case 602: // Heavy snow - case 611: // Sleet - case 612: // Shower sleet - case 621: // Shower snow - case 622: // Heavy shower snow - case 615: // Light rain and snow - case 616: // Rain and snow - return 75; - // Group 7xx: Atmosphere - case 701: // Mist - case 711: // Smoke - case 721: // Haze - case 731: // Sandcase/dust whirls - case 741: // Fog - case 751: // Sand - case 761: // Dust - return -1; - case 762: // Volcanic ash - return 100; - case 771: // Squalls - case 781: // Tornado - case 900: // Tornado - // Group 800: Clouds & Clear - case 800: // Clear sky - return 0; - case 801: // Few clouds - return 25; - case 802: // Scattered clouds - return 50; - case 803: // Broken clouds - return 75; - case 804: // Overcast clouds - return 100; - // Group 90x: Extreme - case 901: // Tropical storm - case 903: // Cold - case 904: // Hot - case 905: // Windy - case 906: // Hail - return 75; - // Group 9xx: Additional - case 951: // Calm - case 952: // Light breeze - case 953: // Gentle breeze - case 954: // Moderate breeze - case 955: // Fresh breeze - case 956: // Strong breeze - case 957: // High windcase near gale - case 958: // Gale - case 959: // Severe gale - case 960: // Storm - case 961: // Violent storm - case 902: // Hurricane - case 962: // Hurricane - default: - return -1; - } - } - - ; - - /** - * Visibility obscuration types - */ - public enum ObscurationType { - /** - * No obscuration - */ - None(0), - /** - * Water particles suspended in the air; low visibility; does not fall - */ - Fog(1), - /** - * Tiny, dry particles in the air; invisible to the eye; opalescent - */ - Haze(2), - /** - * Small fire-created particles suspended in the air - */ - Smoke(3), - /** - * Fine rock powder, from for example volcanoes - */ - Ash(4), - /** - * Fine particles of earth suspended in the air by the wind - */ - Dust(5), - /** - * Fine particles of sand suspended in the air by the wind - */ - Sand(6), - /** - * Water particles suspended in the air; low-ish visibility; temperature is near dewpoint - */ - Mist(7), - /** - * This is special in the sense that the thing falling down is doing the obscuration - */ - Precipitation(8), - Length(9); - - public final int value; - - ObscurationType(int value) { - this.value = value; - } - } - - /** - * Types of precipitation - */ - public enum PrecipitationType { - /** - * No precipitation - *

- * Theoretically we could just _not_ send the event, but then - * how do we differentiate between no precipitation and - * no information about precipitation - */ - None(0), - /** - * Drops larger than a drizzle; also widely separated drizzle - */ - Rain(1), - /** - * Fairly uniform rain consisting of fine drops - */ - Drizzle(2), - /** - * Rain that freezes upon contact with objects and ground - */ - FreezingRain(3), - /** - * Rain + hail; ice pellets; small translucent frozen raindrops - */ - Sleet(4), - /** - * Larger ice pellets; falling separately or in irregular clumps - */ - Hail(5), - /** - * Hail with smaller grains of ice; mini-snowballs - */ - SmallHail(6), - /** - * Snow... - */ - Snow(7), - /** - * Frozen drizzle; very small snow crystals - */ - SnowGrains(8), - /** - * Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions - */ - IceCrystals(9), - /** - * It's raining down ash, e.g. from a volcano - */ - Ash(10), - Length(11); - - public final int value; - - PrecipitationType(int value) { - this.value = value; - } - } - - /** - * These are special events that can "enhance" the "experience" of existing weather events - */ - public enum SpecialType { - /** - * Strong wind with a sudden onset that lasts at least a minute - */ - Squall(0), - /** - * Series of waves in a water body caused by the displacement of a large volume of water - */ - Tsunami(1), - /** - * Violent; rotating column of air - */ - Tornado(2), - /** - * Unplanned; unwanted; uncontrolled fire in an area - */ - Fire(3), - /** - * Thunder and/or lightning - */ - Thunder(4), - Length(5); - public final int value; - - SpecialType(int value) { - this.value = value; - } - } - - /** - * These are used for weather timeline manipulation - * that isn't just adding to the stack of weather events - */ - public enum ControlCodes { - /** - * How much is stored already - */ - GetLength(0), - /** - * This wipes the entire timeline - */ - DelTimeline(1), - /** - * There's a currently valid timeline event with the given type - */ - HasValidEvent(3), - Length(4); - - public final int value; - - ControlCodes(int value) { - this.value = value; - } - } - - /** - * Events have types - * then they're easier to parse after sending them over the air - */ - public enum EventType { - /** - * @see WeatherData.Obscuration - */ - Obscuration(0), - /** - * @see WeatherData.Precipitation - */ - Precipitation(1), - /** - * @see WeatherData.Wind - */ - Wind(2), - /** - * @see WeatherData.Temperature - */ - Temperature(3), - /** - * @see WeatherData.AirQuality - */ - AirQuality(4), - /** - * @see WeatherData.Special - */ - Special(5), - /** - * @see WeatherData.Pressure - */ - Pressure(6), - /** - * @see WeatherData.Location - */ - Location(7), - /** - * @see WeatherData.Clouds - */ - Clouds(8), - /** - * @see WeatherData.Humidity - */ - Humidity(9), - Length(10); - - public final int value; - - EventType(int value) { - this.value = value; - } - } - - ; - - /** - * Valid event query - */ - static public class ValidEventQuery { - ControlCodes code = ControlCodes.HasValidEvent; - EventType eventType; - } - - ; - - /** - * The header used for further parsing - */ - static public class TimelineHeader { - /** - * UNIX timestamp - */ - long timestamp; - /** - * Time in seconds until the event expires - *

- * 32 bits ought to be enough for everyone - *

- * If there's a newer event of the same type then it overrides this one, even if it hasn't expired - */ - int expires; - /** - * What type of weather-related event - */ - EventType eventType; - } - - /** - * Specifies how cloudiness is stored - */ - static public class Clouds extends TimelineHeader { - /** - * Cloud coverage in percentage, 0-100% - */ - byte amount; - } - - /** - * Specifies how obscuration is stored - */ - static public class Obscuration extends TimelineHeader { - /** - * Type - */ - ObscurationType type; - /** - * Visibility distance in meters (0-65535) - */ - int amount; - } - - /** - * Specifies how precipitation is stored - */ - static public class Precipitation extends TimelineHeader { - /** - * Type - */ - PrecipitationType type; - /** - * How much is it going to rain? In millimeters (0-255) - */ - int amount; - } - - /** - * How wind speed is stored - *

- * In order to represent bursts of wind instead of constant wind, - * you have minimum and maximum speeds. - *

- * As direction can fluctuate wildly and some watchfaces might wish to display it nicely, - * we're following the aerospace industry weather report option of specifying a range. - */ - static public class Wind extends TimelineHeader { - /** - * Meters per second (0-255) - */ - byte speedMin; - /** - * Meters per second (0-255) - */ - byte speedMax; - /** - * Unitless direction between 0-255; approximately 1 unit per 0.71 degrees - */ - byte directionMin; - /** - * Unitless direction between 0-255; approximately 1 unit per 0.71 degrees - */ - byte directionMax; - } - - /** - * How temperature is stored - *

- * As it's annoying to figure out the dewpoint on the watch, - * please send it from the companion - *

- * We don't do floats, microdegrees are not useful. Make sure to multiply. - */ - static public class Temperature extends TimelineHeader { - /** - * Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250, 0-65535) - */ - short temperature; - /** - * Dewpoint °C but multiplied by 100 (e.g. -12.50°C becomes -1250, 0-65535) - */ - short dewPoint; - } - - /** - * How location info is stored - *

- * This can be mostly static with long expiration, - * as it usually is, but it could change during a trip for ex. - * so we allow changing it dynamically. - *

- * Location info can be for some kind of map watchface - * or daylight calculations, should those be required. - */ - static public class Location extends TimelineHeader { - /** - * Location name - */ - String location; - /** - * Altitude relative to sea level in meters (0-65535) - */ - short altitude; - /** - * Latitude, EPSG:3857 (Google Maps, Openstreetmaps datum, 0-4294967295) - */ - int latitude; - /** - * Longitude, EPSG:3857 (Google Maps, Openstreetmaps datum, 0-4294967295) - */ - int longitude; - } - - /** - * How humidity is stored - */ - static public class Humidity extends TimelineHeader { - /** - * Relative humidity, 0-100% - */ - byte humidity; - } - - /** - * How air pressure is stored - */ - static public class Pressure extends TimelineHeader { - /** - * Air pressure in hectopascals (hPa, 0-65535) - */ - short pressure; - } - - /** - * How special events are stored - */ - static public class Special extends TimelineHeader { - /** - * Special event's type - */ - SpecialType type; - } - - /** - * How air quality is stored - *

- * These events are a bit more complex because the topic is not simple, - * the intention is to heavy-lift the annoying preprocessing from the watch - * this allows watchface or watchapp makers to generate accurate alerts and graphics - *

- * If this needs further enforced standardization, pull requests are welcome - */ - static public class AirQuality extends TimelineHeader { - /** - * The name of the pollution - *

- * for the sake of better compatibility with watchapps - * that might want to use this data for say visuals - * don't localize the name. - *

- * Ideally watchapp itself localizes the name, if it's at all needed. - *

- * E.g. - * For generic ones use "PM0.1", "PM5", "PM10" - * For chemical compounds use the molecular formula e.g. "NO2", "CO2", "O3" - * For pollen use the genus, e.g. "Betula" for birch or "Alternaria" for that mold's spores - */ - String polluter; - /** - * Amount of the pollution in SI units, - * otherwise it's going to be difficult to create UI, alerts - * and so on and for. - *

- * See more: - * https://ec.europa.eu/environment/air/quality/standards.htm - * http://www.ourair.org/wp-content/uploads/2012-aaqs2.pdf - *

- * Example units: - * count/m³ for pollen - * µgC/m³ for micrograms of organic carbon - * µg/m³ sulfates, PM0.1, PM1, PM2, PM10 and so on, dust - * mg/m³ CO2, CO - * ng/m³ for heavy metals - *

- * List is not comprehensive, should be improved. - * The current ones are what watchapps assume. - *

- * Note: ppb and ppm to concentration should be calculated on the companion, using - * the correct formula (taking into account temperature and air pressure) - *

- * Note2: The amount is off by times 100, for two decimal places of precision. - * E.g. 54.32µg/m³ is 5432 - */ - int amount; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java index 1884c0d3c..1950833ee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pinetime/PineTimeJFSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2022 Andreas Shimokawa, Carsten Pfeiffer, JF, Sebastian +/* Copyright (C) 2016-2021 Andreas Shimokawa, Carsten Pfeiffer, JF, Sebastian Kranz, Taavi Eomäe This file is part of Gadgetbridge. @@ -17,25 +17,16 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.pinetime; -import static nodomain.freeyourgadget.gadgetbridge.devices.pinetime.weather.WeatherData.mapOpenWeatherConditionToCloudCover; -import static nodomain.freeyourgadget.gadgetbridge.devices.pinetime.weather.WeatherData.mapOpenWeatherConditionToPineTimeObscuration; -import static nodomain.freeyourgadget.gadgetbridge.devices.pinetime.weather.WeatherData.mapOpenWeatherConditionToPineTimePrecipitation; -import static nodomain.freeyourgadget.gadgetbridge.devices.pinetime.weather.WeatherData.mapOpenWeatherConditionToPineTimeSpecial; - import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Intent; import android.net.Uri; -import android.os.Build; import android.widget.Toast; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Calendar; @@ -45,8 +36,7 @@ import java.util.Locale; import java.util.TimeZone; import java.util.UUID; -import co.nstant.in.cbor.CborBuilder; -import co.nstant.in.cbor.CborEncoder; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import no.nordicsemi.android.dfu.DfuLogListener; import no.nordicsemi.android.dfu.DfuProgressListener; import no.nordicsemi.android.dfu.DfuProgressListenerAdapter; @@ -65,7 +55,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeActivitySam import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeDFUService; import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeInstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFConstants; -import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.weather.WeatherData; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.PineTimeActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.User; @@ -246,7 +235,6 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL addSupportedService(GattService.UUID_SERVICE_DEVICE_INFORMATION); addSupportedService(GattService.UUID_SERVICE_BATTERY_SERVICE); addSupportedService(PineTimeJFConstants.UUID_SERVICE_MUSIC_CONTROL); - addSupportedService(PineTimeJFConstants.UUID_SERVICE_WEATHER); addSupportedService(PineTimeJFConstants.UUID_CHARACTERISTIC_ALERT_NOTIFICATION_EVENT); addSupportedService(PineTimeJFConstants.UUID_SERVICE_MOTION); @@ -493,9 +481,6 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL batteryInfoProfile.requestBatteryInfo(builder); batteryInfoProfile.enableNotify(builder, true); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - builder.requestMtu(256); - } return builder; } @@ -666,249 +651,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL @Override public void onSendWeather(WeatherSpec weatherSpec) { - if (this.firmwareVersionMajor != 1 || this.firmwareVersionMinor <= 7) { - // Not supported - return; - } else { - if (weatherSpec.location != null) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 6) // 6h - .put("EventType", WeatherData.EventType.Location.value) - .put("Location", weatherSpec.location) - .put("Altitude", 0) - .put("Latitude", 0) - .put("Longitude", 0) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - builder.queue(getQueue()); - } - - // Current condition - if (weatherSpec.currentCondition != null) { - // We can't do anything with this? - } - - // Current humidity - if (weatherSpec.currentHumidity > 0) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 6) // 6h this should be the weather provider's interval, really - .put("EventType", WeatherData.EventType.Humidity.value) - .put("Humidity", (int) weatherSpec.currentHumidity) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - // Current temperature - if (weatherSpec.currentTemp >= -273.15) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 6) // 6h this should be the weather provider's interval, really - .put("EventType", WeatherData.EventType.Temperature.value) - .put("Temperature", (int) (weatherSpec.currentTemp * 100)) - .put("DewPoint", (int) (-32768)) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - // 24h temperature forecast - if (weatherSpec.todayMinTemp >= -273.15 && - weatherSpec.todayMaxTemp >= -273.15) { // Some sanity checking, should really be nullable - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 60 * 24) // 24h, because the temperature is today's - .put("EventType", WeatherData.EventType.Temperature.value) - .put("Temperature", (int) (((weatherSpec.todayMinTemp + weatherSpec.todayMaxTemp) / 2) * 100)) - .put("DewPoint", (int) (-32768)) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - // Wind speed - if (weatherSpec.windSpeed != 0.0f) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 60 * 6) // 6h - .put("EventType", WeatherData.EventType.Wind.value) - .put("SpeedMin", (int) (weatherSpec.windSpeed / 60 / 60 * 1000)) - .put("SpeedMax", (int) (weatherSpec.windSpeed / 60 / 60 * 1000)) - .put("DirectionMin", (int) (0.71 * weatherSpec.windDirection)) - .put("DirectionMax", (int) (0.71 * weatherSpec.windDirection)) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - // Current weather condition - if (mapOpenWeatherConditionToPineTimePrecipitation(weatherSpec.currentConditionCode) != WeatherData.PrecipitationType.Length) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 60 * 6) // 6h - .put("EventType", WeatherData.EventType.Precipitation.value) - .put("Type", (int) mapOpenWeatherConditionToPineTimePrecipitation(weatherSpec.currentConditionCode).value) - .put("Amount", (int) 0) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - if (mapOpenWeatherConditionToPineTimeObscuration(weatherSpec.currentConditionCode) != WeatherData.ObscurationType.Length) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 60 * 6) // 6h - .put("EventType", WeatherData.EventType.Obscuration.value) - .put("Type", (int) mapOpenWeatherConditionToPineTimeObscuration(weatherSpec.currentConditionCode).value) - .put("Amount", (int) 65535) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - if (mapOpenWeatherConditionToPineTimeSpecial(weatherSpec.currentConditionCode) != WeatherData.SpecialType.Length) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 60 * 6) // 6h - .put("EventType", WeatherData.EventType.Special.value) - .put("Type", mapOpenWeatherConditionToPineTimeSpecial(weatherSpec.currentConditionCode).value) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - if (mapOpenWeatherConditionToCloudCover(weatherSpec.currentConditionCode) != -1) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - new CborEncoder(baos).encode(new CborBuilder() - .startMap() // This map is not fixed-size, which is not great, but it might come in a library update - .put("Timestamp", System.currentTimeMillis() / 1000L) - .put("Expires", 60 * 60 * 6) // 6h - .put("EventType", WeatherData.EventType.Clouds.value) - .put("Amount", (int) (mapOpenWeatherConditionToCloudCover(weatherSpec.currentConditionCode))) - .end() - .build() - ); - } catch (Exception e) { - LOG.warn(String.valueOf(e)); - } - byte[] encodedBytes = baos.toByteArray(); - TransactionBuilder builder = createTransactionBuilder("WeatherData"); - safeWriteToCharacteristic(builder, - PineTimeJFConstants.UUID_CHARACTERISTIC_WEATHER_DATA, - encodedBytes); - - builder.queue(getQueue()); - } - - LOG.debug("Wrote weather data"); - } } /** @@ -928,8 +671,6 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL if (characteristic != null && (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) { builder.write(characteristic, data); - } else { - LOG.warn("Tried to write to a characteristic that did not exist or was not writable!"); } } @@ -947,7 +688,7 @@ public class PineTimeJFSupport extends AbstractBTLEDeviceSupport implements DfuL versionCmd.hwVersion = info.getHardwareRevision(); versionCmd.fwVersion = info.getFirmwareRevision(); - if (versionCmd.fwVersion != null && !versionCmd.fwVersion.isEmpty()) { + if(versionCmd.fwVersion != null && !versionCmd.fwVersion.isEmpty()) { // FW version format : "major.minor.patch". Ex : "0.8.2" String[] tokens = StringUtils.split(versionCmd.fwVersion, "."); if (tokens.length == 3) {