From e477d22c88df4bf651f99e60989b974fc5d46259 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 30 Dec 2016 20:14:13 +0100 Subject: [PATCH] Pebble: support the system weather app. - enable/disable weather app from the watchapp list - convert weather data to a format that can be displayed by the system app TODO: send the weather data periodically --- .../AbstractAppManagerFragment.java | 7 ++ .../AppManagerFragmentInstalledApps.java | 3 + .../gadgetbridge/model/Weather.java | 111 ++++++++++++++++++ .../devices/pebble/PebbleIoThread.java | 4 + .../devices/pebble/PebbleProtocol.java | 31 ++++- app/src/main/res/menu/appmanager_context.xml | 6 + app/src/main/res/values/strings.xml | 2 + 7 files changed, 161 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index 3c0de5ae0..50038a9fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -199,6 +199,9 @@ public abstract class AbstractAppManagerFragment extends Fragment { if (baseName.equals("3af858c3-16cb-4561-91e7-f1ad2df8725f")) { cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), "Kickstart (System)", "Pebble Inc.", "", GBDeviceApp.Type.WATCHFACE_SYSTEM)); } + if (baseName.equals(PebbleProtocol.UUID_WEATHER.toString())) { + cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_WEATHER, "Weather (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } } } if (uuids == null) { @@ -367,8 +370,12 @@ public abstract class AbstractAppManagerFragment extends Fragment { case R.id.appmanager_hrm_activate: GBApplication.deviceService().onInstallApp(Uri.parse("fake://hrm")); return true; + case R.id.appmanager_weather_activate: + GBApplication.deviceService().onInstallApp(Uri.parse("fake://weather")); + return true; case R.id.appmanager_health_deactivate: case R.id.appmanager_hrm_deactivate: + case R.id.appmanager_weather_deactivate: GBApplication.deviceService().onAppDelete(selectedApp.getUUID()); return true; case R.id.appmanager_app_configure: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java index 6cf8bfbbc..fe53c32ca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java @@ -28,6 +28,9 @@ public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment if (PebbleUtils.hasHRM(mGBDevice.getModel())) { systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); } + if (PebbleUtils.getFwMajor(mGBDevice.getFirmwareVersion()) >= 4) { + systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WEATHER, "Weather (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } } return systemApps; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java index 59981199a..17c8ebc5e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java @@ -10,6 +10,117 @@ public class Weather { private static final Weather weather = new Weather(); public static Weather getInstance() {return weather;} + public static byte mapToPebbleCondition(int openWeatherMapCondition) { +/* 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 + */ + switch (openWeatherMapCondition) { +//Group 2xx: Thunderstorm + case 200: //thunderstorm with light rain: //11d + case 201: //thunderstorm with rain: //11d + case 202: //thunderstorm with heavy rain: //11d + case 210: //light thunderstorm:: //11d + case 211: //thunderstorm: //11d + case 230: //thunderstorm with light drizzle: //11d + case 231: //thunderstorm with drizzle: //11d + case 232: //thunderstorm with heavy drizzle: //11d + case 212: //heavy thunderstorm: //11d + case 221: //ragged thunderstorm: //11d + return 4; +//Group 3xx: Drizzle + case 300: //light intensity drizzle: //09d + case 301: //drizzle: //09d + case 302: //heavy intensity drizzle: //09d + case 310: //light intensity drizzle rain: //09d + case 311: //drizzle rain: //09d + case 312: //heavy intensity drizzle rain: //09d + case 313: //shower rain and drizzle: //09d + case 314: //heavy shower rain and drizzle: //09d + case 321: //shower drizzle: //09d + case 500: //light rain: //10d + case 501: //moderate rain: //10d + return 3; +//Group 5xx: Rain + case 502: //heavy intensity rain: //10d + case 503: //very heavy rain: //10d + case 504: //extreme rain: //10d + case 511: //freezing rain: //13d + case 520: //light intensity shower rain: //09d + case 521: //shower rain: //09d + case 522: //heavy intensity shower rain: //09d + case 531: //ragged shower rain: //09d + return 4; +//Group 6xx: Snow + case 600: //light snow: //[[file:13d.png]] + case 601: //snow: //[[file:13d.png]] + case 620: //light shower snow: //[[file:13d.png]] + return 2; + case 602: //heavy snow: //[[file:13d.png]] + case 611: //sleet: //[[file:13d.png]] + case 612: //shower sleet: //[[file:13d.png]] + case 621: //shower snow: //[[file:13d.png]] + case 622: //heavy shower snow: //[[file:13d.png]] + return 5; + case 615: //light rain and snow: //[[file:13d.png]] + case 616: //rain and snow: //[[file:13d.png]] + return 8; +//Group 7xx: Atmosphere + case 701: //mist: //[[file:50d.png]] + case 711: //smoke: //[[file:50d.png]] + case 721: //haze: //[[file:50d.png]] + case 731: //sandcase dust whirls: //[[file:50d.png]] + case 741: //fog: //[[file:50d.png]] + case 751: //sand: //[[file:50d.png]] + case 761: //dust: //[[file:50d.png]] + case 762: //volcanic ash: //[[file:50d.png]] + case 771: //squalls: //[[file:50d.png]] + case 781: //tornado: //[[file:50d.png]] + case 900: //tornado + return 6; +//Group 800: Clear + case 800: //clear sky: //[[file:01d.png]] [[file:01n.png]] + return 7; +//Group 80x: Clouds + case 801: //few clouds: //[[file:02d.png]] [[file:02n.png]] + case 802: //scattered clouds: //[[file:03d.png]] [[file:03d.png]] + case 803: //broken clouds: //[[file:04d.png]] [[file:03d.png]] + case 804: //overcast clouds: //[[file:04d.png]] [[file:04d.png]] + return 0; +//Group 90x: Extreme + case 901: //tropical storm + case 903: //cold + case 904: //hot + 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 6; + + } + } public static int mapToYahooCondition(int openWeatherMapCondition) { // openweathermap.org conditions: // http://openweathermap.org/weather-conditions diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 035fa17b6..923ee95cc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -613,6 +613,10 @@ class PebbleIoThread extends GBDeviceIoThread { write(mPebbleProtocol.encodeActivateHRM(true)); return; } + if (uri.equals(Uri.parse("fake://weather"))) { + write(mPebbleProtocol.encodeActivateWeather(true)); + return; + } if (mIsInstalling) { return; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index 39bd0cee2..ea954c151 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -38,7 +38,9 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; +import ru.gelin.android.weather.notification.ParcelableWeather2; public class PebbleProtocol extends GBDeviceProtocol { @@ -370,6 +372,7 @@ public class PebbleProtocol extends GBDeviceProtocol { public static final UUID UUID_PEBBLE_HEALTH = UUID.fromString("36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c"); // FIXME: store somewhere else, this is also accessed by other code public static final UUID UUID_WORKOUT = UUID.fromString("fef82c82-7176-4e22-88de-35a3fc18d43f"); // FIXME: store somewhere else, this is also accessed by other code + public static final UUID UUID_WEATHER = UUID.fromString("61b22bc8-1e29-460d-a236-3fe409a439ff"); // FIXME: store somewhere else, this is also accessed by other code private static final UUID UUID_GBPEBBLE = UUID.fromString("61476764-7465-7262-6469-656775527a6c"); private static final UUID UUID_MORPHEUZ = UUID.fromString("5be44f1d-d262-4ea6-aa30-ddbec1e3cab2"); private static final UUID UUID_WHETHERNEAT = UUID.fromString("3684003b-a685-45f9-a713-abc6364ba051"); @@ -545,7 +548,8 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeActivateWeather(true); } else { //return encodeWeatherPin(ts, "Weather", "1°/-1°", "Gadgetbridge is Sunny", "Berlin", 37); - return encodeWeatherForecast(ts, "Berlin", 0, 5, -5, 1, "Sexy", 7, 2, 1); + //return encodeWeatherForecast(ts, "Berlin", 0, 5, -5, 1, "Sexy", 7, 2, 1); + return encodeWeatherForecast(ts); } } @@ -1126,6 +1130,24 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array()); } + private byte[] encodeWeatherForecast(int timestamp) { + ParcelableWeather2 weather = Weather.getInstance().getWeather2(); + if (weather != null) { + return encodeWeatherForecast(timestamp, + weather.location, + weather.currentTemp - 273, + weather.todayHighTemp - 273, + weather.todayLowTemp - 273, + Weather.mapToPebbleCondition(weather.currentConditionCode), + weather.currentCondition, + weather.forecastHighTemp - 273, + weather.forecastLowTemp - 273, + Weather.mapToPebbleCondition(weather.forecastConditionCode) + ); + } + return null; + } + private byte[] encodeWeatherForecast(int timestamp, String location, int tempNow, int tempHighToday, int tempLowToday, int conditionCodeToday, String conditionToday, int tempHighTomorrow, int tempLowTomorrow, int conditionCodeTomorrow) { final short WEATHER_FORECAST_LENGTH = 20; @@ -1148,10 +1170,10 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.order(ByteOrder.LITTLE_ENDIAN); buf.put((byte) 3); // unknown, always 3? buf.putShort((short) tempNow); - buf.put((byte) 1); + buf.put((byte) conditionCodeToday); buf.putShort((short) tempHighToday); buf.putShort((short) tempLowToday); - buf.put((byte) 4); + buf.put((byte) conditionCodeTomorrow); buf.putShort((short) tempHighTomorrow); buf.putShort((short) tempLowTomorrow); buf.putInt(timestamp); @@ -1392,6 +1414,9 @@ public class PebbleProtocol extends GBDeviceProtocol { if (UUID_WORKOUT.equals(uuid)) { return encodeActivateHRM(false); } + if (UUID_WEATHER.equals(uuid)) { //TODO: probably it wasn't present in firmware 3 + return encodeActivateWeather(false); + } return encodeBlobdb(uuid, BLOBDB_DELETE, BLOBDB_APP, null); } else { ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_REMOVEAPP_2X); diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml index 436454388..766f6c5a2 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -21,6 +21,12 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9d2e3e2b..6430085cc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,6 +30,8 @@ Deactivate Activate HRM Deactivate HRM + Activate system weather app + Deactivate system weather app Configure Move to top