From d902c67aad00635fe8a2a1e6f18a50e81fe875bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Greffier?= Date: Wed, 23 Jan 2019 21:09:32 +0100 Subject: [PATCH 1/5] Gradle jcenter issue See https://issuetracker.google.com/issues/80362794 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6b7dab00b..50dc8bb0e 100644 --- a/build.gradle +++ b/build.gradle @@ -15,10 +15,10 @@ buildscript { allprojects { repositories { - jcenter() maven { url "https://jitpack.io" } google() + jcenter() } } From 2e7c7d8e52f5598f7aa2a9ccf337e92e484af357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Greffier?= Date: Wed, 23 Jan 2019 21:19:25 +0100 Subject: [PATCH 2/5] MIBCS discovery --- .../miscale2/MiScale2DeviceCoordinator.java | 160 ++++++++++++++++++ .../gadgetbridge/model/DeviceType.java | 1 + .../gadgetbridge/util/DeviceHelper.java | 2 + app/src/main/res/values/strings.xml | 1 + 4 files changed, 164 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java new file mode 100644 index 000000000..d7ff724b6 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java @@ -0,0 +1,160 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.miscale2; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; + +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +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.btle.GattService; + +public class MiScale2DeviceCoordinator extends AbstractDeviceCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(MiScale2DeviceCoordinator.class); + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + try { + BluetoothDevice device = candidate.getDevice(); + String name = device.getName(); + if (name != null && name.equalsIgnoreCase("MIBCS")) { + return DeviceType.MISCALE2; + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return DeviceType.UNKNOWN; + } + + @NonNull + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + ParcelUuid bodyCompositionService = new ParcelUuid(GattService.UUID_SERVICE_BODY_COMPOSITION); + + ScanFilter.Builder builder = new ScanFilter.Builder(); + builder.setServiceUuid(bodyCompositionService); + + int manufacturerId = 0x0157; // Huami + builder.setManufacturerData(manufacturerId, new byte[6], new byte[6]); + + return Collections.singletonList(builder.build()); + } + + @Override + public int getBondingStyle(GBDevice device) { + return super.BONDING_STYLE_NONE; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.MISCALE2; + } + + @Nullable + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public int getAlarmSlotCount() { + return 0; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public String getManufacturer() { + return "Huami"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + public boolean supportsCalendarEvents() { + return false; + } + + @Override + public boolean supportsRealtimeData() { + return false; + } + + @Override + public boolean supportsWeather() { + return false; + } + + @Override + public boolean supportsFindDevice() { + return false; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 0ca18cfde..8e18c0be8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -52,6 +52,7 @@ public enum DeviceType { ROIDMI(110, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi), ROIDMI3(112, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi3), CASIOGB6900(120, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_casiogb6900), + MISCALE2(131, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_miscale2), TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 4ecbb6ed4..d79e3c788 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -53,6 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2HRXCoor import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.id115.ID115Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.miscale2.MiScale2DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; @@ -196,6 +197,7 @@ public class DeviceHelper { private List createCoordinators() { List result = new ArrayList<>(); + result.add(new MiScale2DeviceCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new AmazfitBipCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new AmazfitCorCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new MiBand3Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 304fc7900..be6470246 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -629,6 +629,7 @@ Roidmi Roidmi 3 Casio GB-6900 + Mi Scale 2 Choose export location Gadgetbridge notifications From 973bd1ad87867468fecb4805414da1e5917e381a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Greffier?= Date: Tue, 29 Jan 2019 22:37:41 +0100 Subject: [PATCH 3/5] MIBCS weight mesure inital support --- .../service/DeviceSupportFactory.java | 3 + .../miscale2/MiScale2DeviceSupport.java | 290 ++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 82c16790c..f9ac9e574 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -37,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.id115.ID115Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miscale2.MiScale2DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport; @@ -174,6 +175,8 @@ public class DeviceSupportFactory { break; case CASIOGB6900: deviceSupport = new ServiceDeviceSupport(new CasioGB6900DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + case MISCALE2: + deviceSupport = new ServiceDeviceSupport(new MiScale2DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; } if (deviceSupport != null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java new file mode 100644 index 000000000..f7baa1922 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java @@ -0,0 +1,290 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miscale2; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Intent; +import android.net.Uri; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +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.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class MiScale2DeviceSupport extends AbstractBTLEDeviceSupport { + + private static final Logger LOG = LoggerFactory.getLogger(MiScale2DeviceSupport.class); + private static final String UNIT_KG = "kg"; + private static final String UNIT_LBS = "lb"; + private static final String UNIT_JIN = "jīn"; + private final DeviceInfoProfile deviceInfoProfile; + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private final IntentListener mListener = new IntentListener() { + @Override + public void notify(Intent intent) { + String s = intent.getAction(); + if (s.equals(DeviceInfoProfile.ACTION_DEVICE_INFO)) { + handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO)); + } + } + }; + + public MiScale2DeviceSupport() { + super(LOG); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); + addSupportedService(GattService.UUID_SERVICE_DEVICE_INFORMATION); + addSupportedService(GattService.UUID_SERVICE_BODY_COMPOSITION); + addSupportedService(UUID.fromString("00001530-0000-3512-2118-0009af100700")); + + deviceInfoProfile = new DeviceInfoProfile<>(this); + deviceInfoProfile.addListener(mListener); + addSupportedProfile(deviceInfoProfile); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); + + LOG.debug("Requesting Device Info!"); + deviceInfoProfile.requestDeviceInfo(builder); + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); + + // Weight and body composition + builder.setGattCallback(this); + builder.notify(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_BODY_COMPOSITION_MEASUREMENT), true); + + return builder; + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + super.onCharacteristicChanged(gatt, characteristic); + + UUID characteristicUUID = characteristic.getUuid(); + if (characteristicUUID.equals(GattCharacteristic.UUID_CHARACTERISTIC_BODY_COMPOSITION_MEASUREMENT)) { + final byte[] data = characteristic.getValue(); + + boolean stabilized = testBit(data[1], 5) && !testBit(data[1], 7); + boolean isLbs = testBit(data[1], 0); + boolean isJin = testBit(data[1], 4); + boolean isKg = !(isLbs && isJin); + String unit = ""; + if (isKg) { + unit = UNIT_KG; + } else if (isLbs) { + unit = UNIT_LBS; + } else if (isJin) { + unit = UNIT_JIN; + } + + if (stabilized) { + int year = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 2); + int month = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 4); + int day = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 5); + int hour = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 6); + int minute = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 7); + int second = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 8); + Calendar c = GregorianCalendar.getInstance(); + c.set(year, month - 1, day, hour, minute, second); + Date date = c.getTime(); + float weight = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 11) / (isKg ? 200.0f : 100.0f); + handleWeightInfo(date, weight, unit); + } + + return true; + } + + return false; + } + + private boolean testBit(byte value, int offset) { + return ((value >> offset) & 1) == 1; + } + + private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { + LOG.warn("Device info: " + info); + versionCmd.hwVersion = info.getHardwareRevision(); + versionCmd.fwVersion = info.getSoftwareRevision(); + handleGBDeviceEvent(versionCmd); + } + + private void handleWeightInfo(Date date, float weight, String unit) { + // TODO + LOG.warn("Weight info: " + weight + unit); + GB.toast(weight + unit, Toast.LENGTH_SHORT, GB.INFO); + } + + @Override + public boolean useAutoConnect() { + return false; + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + + } + + @Override + public void onDeleteNotification(int id) { + + } + + @Override + public void onSetTime() { + + } + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onSetCallState(CallSpec callSpec) { + + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + + } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppInfoReq() { + + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + + } + + @Override + public void onAppDelete(UUID uuid) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config, Integer id) { + + } + + @Override + public void onAppReorder(UUID[] uuids) { + + } + + @Override + public void onFetchRecordedData(int dataTypes) { + + } + + @Override + public void onReset(int flags) { + + } + + @Override + public void onHeartRateTest() { + + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + + } + + @Override + public void onFindDevice(boolean start) { + + } + + @Override + public void onSetConstantVibration(int integer) { + + } + + @Override + public void onScreenshotReq() { + + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + + } + + @Override + public void onSendConfiguration(String config) { + + } + + @Override + public void onTestNewFunction() { + + } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } +} From 4321a29e464341924b360b74a8d03b152565667d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Greffier?= Date: Thu, 31 Jan 2019 15:49:42 +0100 Subject: [PATCH 4/5] MIBCS update from android.support to androidx --- .../devices/miscale2/MiScale2DeviceCoordinator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java index d7ff724b6..b64560e16 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java @@ -8,8 +8,8 @@ import android.content.Context; import android.net.Uri; import android.os.Build; import android.os.ParcelUuid; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From a69c44d4b2194e047da573041297a3bce754817e Mon Sep 17 00:00:00 2001 From: jfgreffier Date: Fri, 1 Mar 2019 11:25:42 +0100 Subject: [PATCH 5/5] Revert "Gradle jcenter issue See https://issuetracker.google.com/issues/80362794" This reverts commit d902c67aad00635fe8a2a1e6f18a50e81fe875bf. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 50dc8bb0e..6b7dab00b 100644 --- a/build.gradle +++ b/build.gradle @@ -15,10 +15,10 @@ buildscript { allprojects { repositories { + jcenter() maven { url "https://jitpack.io" } google() - jcenter() } }