From 3a2b718f09836b2c39c6ca8f18461bf46e3df10d Mon Sep 17 00:00:00 2001 From: mamutcho Date: Sat, 9 Nov 2019 22:16:10 +0200 Subject: [PATCH] Blood Pressure calibration --- .../activities/SettingsActivity.java | 13 +++ .../watchxplus/WatchXPlusConstants.java | 6 +- .../WatchXPlusDeviceCoordinator.java | 9 +- .../watchxplus/WatchXPlusDeviceSupport.java | 106 +++++++++++++----- app/src/main/res/values-bg/strings.xml | 5 +- app/src/main/res/values/strings.xml | 5 + app/src/main/res/xml/preferences.xml | 24 ++++ 7 files changed, 128 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 25fff1030..b12a71711 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -58,6 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActi import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimePreferenceActivity; import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsPreferencesActivity; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.service.devices.lenovo.watchxplus.WatchXPlusDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -90,6 +91,7 @@ public class SettingsActivity extends AbstractSettingsActivity { Prefs prefs = GBApplication.getPrefs(); Preference pref = findPreference("notifications_generic"); + pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { Intent enableIntent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); @@ -209,6 +211,17 @@ public class SettingsActivity extends AbstractSettingsActivity { }); + pref = findPreference("watchxplus_button_BP_calibration"); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + LOG.info(" uhaaaa "); + preference.setSummary("Calibrating, please wait... (if no result after 15s. re-run)"); + GBApplication.deviceService().onSendConfiguration("BP_CAL"); + return true; + } + }); + final Preference unit = findPreference(PREF_MEASUREMENT_SYSTEM); unit.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java index bc25a933e..42a739a12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java @@ -41,6 +41,8 @@ public final class WatchXPlusConstants extends LenovoWatchConstants { public static final String PREF_IS_BP_CALIBRATED = "watchxplus_is_bp_calibrated"; public static final String PREF_BUTTON_REJECT = "watchxplus_button_reject"; public static final String PREF_SHAKE_REJECT = "watchxplus_shake_reject"; + public static final String PREF_BP_CAL_LOW = "pref_wxp_bp_calibration_low"; + public static final String PREF_BP_CAL_HIGH = "pref_wxp_bp_calibration_high"; // time format constants @@ -64,6 +66,7 @@ public final class WatchXPlusConstants extends LenovoWatchConstants { public static final byte[] CMD_NOTIFICATION_CANCEL = new byte[]{0x03, 0x04}; public static final byte[] CMD_NOTIFICATION_SETTINGS = new byte[]{0x03, 0x02}; public static final byte[] CMD_DO_NOT_DISTURB_SETTINGS = new byte[]{0x03, 0x61}; + public static final byte[] CMD_POWER_MODE = new byte[]{0x03, -0x7F}; //bArr[8] - 0 normal, 1 poser save, 2 green public static final byte[] CMD_FITNESS_GOAL_SETTINGS = new byte[]{0x10, 0x02}; public static final byte[] CMD_DAY_STEPS_INFO = new byte[]{0x10, 0x03}; @@ -77,10 +80,11 @@ public final class WatchXPlusConstants extends LenovoWatchConstants { public static final byte[] RESP_DISCONNECT_REMIND = new byte[]{0x08, 0x00, 0x11}; public static final byte[] RESP_IS_BP_CALIBRATED = new byte[]{0x08, 0x05, 0x0B}; public static final byte[] RESP_BUTTON_WHILE_RING = new byte[]{0x04, 0x03, 0x03}; + public static final byte[] RESP_BP_CALIBRATION = new byte[]{0x08, 0x05, 0x0C}; public static final byte[] RESP_AUTHORIZATION_TASK = new byte[]{0x01, 0x01, 0x05}; public static final byte[] RESP_DAY_STEPS_INDICATOR = new byte[]{0x08, 0x10, 0x03}; - public static final byte[] RESP_HEARTRATE = new byte[]{-0x80, 0x15, 0x03}; + public static final byte[] RESP_HEARTRATE = new byte[]{(byte) 0x80, 0x15, 0x03}; public static final byte[] RESP_DATA_COUNT = new byte[]{0x08, (byte)0xF0, 0x10}; public static final byte[] RESP_DATA_DETAILS = new byte[]{0x08, (byte)0xF0, 0x11}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java index 6814968fd..dd24b6760 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java @@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.lenovo.watchxplus.WatchXPlusDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.GBApplication.getContext; @@ -223,14 +224,6 @@ Prefs from device settings on main page } } - public static byte getBPCalibrationStatus(SharedPreferences sharedPrefs) { - String timeMode = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); - if (timeMode.equals(getContext().getString(R.string.p_timeformat_24h))) { - return WatchXPlusConstants.ARG_SET_TIMEMODE_24H; - } else { - return WatchXPlusConstants.ARG_SET_TIMEMODE_12H; - } - } /* Values from device specific settings page */ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java index fe5d55256..590fa09c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java @@ -25,12 +25,18 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.net.Uri; -import android.os.CountDownTimer; import android.os.Handler; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; import androidx.annotation.IntRange; +import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.snackbar.Snackbar; + import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +56,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; @@ -64,7 +71,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.WatchXPlusActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.WatchXPlusHealthActivityOverlay; import nodomain.freeyourgadget.gadgetbridge.entities.WatchXPlusHealthActivityOverlayDao; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; -import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -77,7 +83,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; @@ -87,12 +92,13 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateA import nodomain.freeyourgadget.gadgetbridge.service.devices.lenovo.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; -import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; +import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { - + protected static Prefs prefs = GBApplication.getPrefs(); private boolean needsAuth; private int sequenceNumber = 0; private boolean isCalibrationActive = false; @@ -568,6 +574,8 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } } + + @Override public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { @@ -655,7 +663,6 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onFindDevice(boolean start) { - } @Override @@ -665,7 +672,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { @Override public void onScreenshotReq() { - + sendBloodPressureCalibration(); } @Override @@ -707,11 +714,10 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { getDisconnectReminderStatus(builder); break; case DeviceSettingsPreferenceConst.PREF_TIMEFORMAT: - setTimeMode(builder, sharedPreferences); + setTimeFormat(builder, sharedPreferences); break; - case WatchXPlusConstants.PREF_ALTITUDE: - LOG.info(" ALTITUDE: " + config); - + case "BP_CAL": + sendBloodPressureCalibration(); break; } builder.queue(getQueue()); @@ -739,18 +745,64 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } + CoordinatorLayout coordinatorLayout; + +// check status of blood pressure calibration + private WatchXPlusDeviceSupport sendBloodPressureCalibration() { + try { + int mLowP = prefs.getInt(WatchXPlusConstants.PREF_BP_CAL_LOW, 80); + int mHighP = prefs.getInt(WatchXPlusConstants.PREF_BP_CAL_HIGH, 130); + LOG.warn("Calibrating BP ... LowP=" + mLowP + " HighP="+mHighP); + GB.toast("Calibrating BP...", Toast.LENGTH_LONG, GB.INFO); + + TransactionBuilder builder = performInitialized("bpCalibrate"); + + byte[] command = WatchXPlusConstants.CMD_BP_CALIBRATION; + byte mStart = 0x01; + + byte[] bArr = new byte[5]; + bArr[0] = (byte) mStart; // byte[08] + bArr[1] = (byte) (mHighP >> 8); // byte[09] + bArr[2] = (byte) mHighP; // byte[10] + bArr[3] = (byte) (mLowP >> 8); // byte[11] + bArr[4] = (byte) mLowP; // byte[12] + + builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), + buildCommand(command, + WatchXPlusConstants.TASK, + bArr)); + builder.queue(getQueue()); } catch (IOException e) { + LOG.warn("Unable to send BP Calibration", e); + } + return this; + } + private void handleBloodPressureCalibrationStatus(byte[] value) { - if (Conversion.calculateHigh(value[8]) != 0) { + if (Conversion.fromByteArr16(value[8]) != 0) { WatchXPlusDeviceCoordinator.isBPCalibrated = false; } else { WatchXPlusDeviceCoordinator.isBPCalibrated = true; } } + + + private void handleBloodPressureCalibrationResult(byte[] value) { + if (Conversion.fromByteArr16(value[8]) != 0x00) { + WatchXPlusDeviceCoordinator.isBPCalibrated = false; + GB.toast("Calibrating BP fail", Toast.LENGTH_LONG, GB.ERROR); + } else { + WatchXPlusDeviceCoordinator.isBPCalibrated = true; + int high = Conversion.fromByteArr16(value[9], value[10]); + int low = Conversion.fromByteArr16(value[11], value[12]); + GB.toast("OK. Measured Low:"+low+" high:"+high, Toast.LENGTH_LONG, GB.INFO); + } + } // end check status of blood pressure calibration private void requestBloodPressureMeasurement() { if (!WatchXPlusDeviceCoordinator.isBPCalibrated) { LOG.warn("BP is NOT calibrated"); + GB.toast("BP is not calibrated", Toast.LENGTH_LONG, GB.WARN); return; } try { @@ -836,6 +888,8 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { handleTime(value); } else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_IS_BP_CALIBRATED, 5)) { handleBloodPressureCalibrationStatus(value); + } else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_BP_CALIBRATION, 5)) { + handleBloodPressureCalibrationResult(value); } else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_BUTTON_INDICATOR, 5)) { this.onReverseFindDevice(true); // It looks like WatchXPlus doesn't send this action @@ -874,7 +928,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return true; } else if (WatchXPlusConstants.UUID_CHARACTERISTIC_DATABASE_READ.equals(characteristicUUID)) { - + LOG.info(" Value change for characteristic DATABASE: " + characteristicUUID + " value " + value); handleContentDataChunk(value); return true; } else { @@ -1123,12 +1177,13 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { if (value.length < 11) { LOG.info(" BP Measure started. Waiting for result"); + GB.toast("BP Measure started. Waiting for result...", Toast.LENGTH_LONG, GB.INFO); } else { LOG.info(" Received BP live data"); int high = Conversion.fromByteArr16(value[8], value[9]); int low = Conversion.fromByteArr16(value[10], value[11]); int timestamp = Conversion.fromByteArr16(value[12], value[13], value[14], value[15]); - + GB.toast("Calculated BP data: low: " + low + ", high: " + high, Toast.LENGTH_LONG, GB.INFO); LOG.info(" Calculated BP data: timestamp: " + timestamp + ", high: " + high + ", low: " + low); } } @@ -1341,7 +1396,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(this.getDevice().getAddress()); this.setHeadsUpScreen(transaction, sharedPreferences); // lift wirst to screen on this.setDisconnectReminder(transaction, sharedPreferences); // disconnect reminder - this.setTimeMode(transaction, sharedPreferences); // set time mode 12/24h + this.setTimeFormat(transaction, sharedPreferences); // set time mode 12/24h this.setAltitude(transaction); // set altitude calibration } @@ -1437,7 +1492,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } // set time format - private WatchXPlusDeviceSupport setTimeMode(TransactionBuilder transactionBuilder, byte timeMode) { + private WatchXPlusDeviceSupport setTimeFormat(TransactionBuilder transactionBuilder, byte timeMode) { byte[] bArr = new byte[2]; bArr[0] = 0x01; //byte[08] language - force to English language bArr[1] = timeMode; //byte[09] time @@ -1445,7 +1500,6 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { buildCommand(WatchXPlusConstants.CMD_TIME_LANGUAGE, WatchXPlusConstants.WRITE_VALUE, bArr)); - //LOG.info(" setTimeMode: " + bArr); return this; } @@ -1470,8 +1524,8 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } - private WatchXPlusDeviceSupport setTimeMode(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { - return this.setTimeMode(transactionBuilder, + private WatchXPlusDeviceSupport setTimeFormat(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { + return this.setTimeFormat(transactionBuilder, WatchXPlusDeviceCoordinator.getTimeMode(sharedPreferences)); } @@ -1501,6 +1555,8 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } } + + private static class Conversion { static byte toBcd8(@IntRange(from = 0, to = 99) int value) { int high = (value / 10) << 4; @@ -1518,7 +1574,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return new byte[]{(byte) (value >> 8), (byte) value}; } - static int fromByteArr16(byte... value) { + static int fromByteArr16(byte... value) { // equals calculateHigh int intValue = 0; for (int i2 = 0; i2 < value.length; i2++) { intValue += (value[i2] & 255) << (((value.length - 1) - i2) * 8); @@ -1542,15 +1598,5 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } return i2; } - - static int calculateHigh(byte... bArr) { - int i = 0; - int i2 = 0; - while (i < bArr.length) { - i2 += (bArr[i] & 255) << (((bArr.length - 1) - i) * 8); - i++; - } - return i2; - } } } diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 3b7f192df..5d83bcc25 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -289,8 +289,11 @@ Калибриране на височина Повтори известия за звънене Възможни стойности min=0, max=10 - Известие докато звъни + Известявай докато телефона звъни Изкл. - игнорира, Вкл. - отказ + Калибриране на кръвно налягане + Кръвно налягане DIASTOLIC (долна) + Кръвно налягане SYSTOLIC (горна) Бутона игнорира/отказва повикване Дублира действието на бутона Разклащането игнорира/отказва повикване diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fe73c3cd3..98e0eb93d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,6 +199,11 @@ Button ignore/reject call Duplicates watch button action Shake wrist ignore/reject call + Blood Pressure calibration + Blood Pressure DIASTOLIC (low) + Blood Pressure SYSTOLIC (high) + Begin calibration + Press here to begin calibration WatchXPlus calibration Makibes HR3 settings diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index a44d6e6e5..1b998ef85 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -620,6 +620,30 @@ android:key="watchxplus_altitude" android:title="@string/pref_wxp_title_altitude"/> + + + + + + +