From 7579ba11b19923d4d8f5683c0c4b249fc5671b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Wed, 28 Aug 2024 17:59:15 +0100 Subject: [PATCH] Mi Composition Scale: Persist weight samples --- .../MiCompositionScaleCoordinator.java | 107 ++++++---------- .../miscale/MiSmartScaleCoordinator.java | 8 +- .../MiCompositionScaleDeviceSupport.java | 115 ++++++++++-------- .../devices/miscale/WeightMeasurement.java | 2 +- 4 files changed, 101 insertions(+), 131 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiCompositionScaleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiCompositionScaleCoordinator.java index 6e881dc5c..c3e7be2a8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiCompositionScaleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiCompositionScaleCoordinator.java @@ -17,42 +17,36 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.miscale; -import android.app.Activity; import android.bluetooth.le.ScanFilter; -import android.content.Context; -import android.net.Uri; import android.os.ParcelUuid; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import androidx.annotation.NonNull; import java.util.Collection; import java.util.Collections; import java.util.regex.Pattern; +import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.MiScaleWeightSampleDao; 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.model.WeightSample; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; import nodomain.freeyourgadget.gadgetbridge.service.devices.miscale.MiCompositionScaleDeviceSupport; public class MiCompositionScaleCoordinator extends AbstractBLEDeviceCoordinator { - private static final Logger LOG = LoggerFactory.getLogger(MiCompositionScaleCoordinator.class); - @Override - protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + protected void deleteDevice(@NonNull final GBDevice gbDevice, @NonNull final Device device, @NonNull final DaoSession session) throws GBException { + final Long deviceId = device.getId(); + final QueryBuilder qb = session.getMiScaleWeightSampleDao().queryBuilder(); + qb.where(MiScaleWeightSampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); } @Override @@ -63,61 +57,25 @@ public class MiCompositionScaleCoordinator extends AbstractBLEDeviceCoordinator @NonNull @Override public Collection createBLEScanFilters() { - ParcelUuid bodyCompositionService = new ParcelUuid(GattService.UUID_SERVICE_BODY_COMPOSITION); + final ParcelUuid bodyCompositionService = new ParcelUuid(GattService.UUID_SERVICE_BODY_COMPOSITION); - ScanFilter.Builder builder = new ScanFilter.Builder(); + final ScanFilter.Builder builder = new ScanFilter.Builder(); builder.setServiceUuid(bodyCompositionService); - int manufacturerId = 0x0157; // Huami + final int manufacturerId = 0x0157; // Huami builder.setManufacturerData(manufacturerId, new byte[6], new byte[6]); return Collections.singletonList(builder.build()); } @Override - public int getBondingStyle() { - return super.BONDING_STYLE_NONE; - } - - @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(final GBDevice device) { - return false; - } - - @Override - public int getAlarmSlotCount(GBDevice device) { + public int getBatteryCount() { return 0; } @Override - public boolean supportsHeartRateMeasurement(GBDevice device) { - return false; + public int getBondingStyle() { + return super.BONDING_STYLE_NONE; } @Override @@ -126,32 +84,37 @@ public class MiCompositionScaleCoordinator extends AbstractBLEDeviceCoordinator } @Override - public boolean supportsAppsManagement(final GBDevice device) { + public TimeSampleProvider getWeightSampleProvider(final GBDevice device, final DaoSession session) { + return new MiScaleSampleProvider(device, session); + } + + @Override + public boolean supportsWeightMeasurement() { + return true; + } + + @Override + public boolean supportsActivityTracking() { + return true; + } + + @Override + public boolean supportsActivityTabs() { return false; } @Override - public Class getAppsManagementActivity() { - return null; - } - - @Override - public boolean supportsCalendarEvents() { + public boolean supportsSleepMeasurement() { return false; } @Override - public boolean supportsRealtimeData() { + public boolean supportsStepCounter() { return false; } @Override - public boolean supportsWeather() { - return false; - } - - @Override - public boolean supportsFindDevice() { + public boolean supportsSpeedzones() { return false; } @@ -161,13 +124,11 @@ public class MiCompositionScaleCoordinator extends AbstractBLEDeviceCoordinator return MiCompositionScaleDeviceSupport.class; } - @Override public int getDeviceNameResource() { return R.string.devicetype_micompositionscale; } - @Override public int getDefaultIconResource() { return R.drawable.ic_device_miscale; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiSmartScaleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiSmartScaleCoordinator.java index 6a8f49242..6ff795f0e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiSmartScaleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale/MiSmartScaleCoordinator.java @@ -71,9 +71,9 @@ public class MiSmartScaleCoordinator extends AbstractBLEDeviceCoordinator { } @Override - protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { - Long deviceId = device.getId(); - QueryBuilder qb = session.getMiScaleWeightSampleDao().queryBuilder(); + protected void deleteDevice(@NonNull final GBDevice gbDevice, @NonNull final Device device, @NonNull final DaoSession session) throws GBException { + final Long deviceId = device.getId(); + final QueryBuilder qb = session.getMiScaleWeightSampleDao().queryBuilder(); qb.where(MiScaleWeightSampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities(); } @@ -88,7 +88,7 @@ public class MiSmartScaleCoordinator extends AbstractBLEDeviceCoordinator { } @Override - public TimeSampleProvider getWeightSampleProvider(GBDevice device, DaoSession session) { + public TimeSampleProvider getWeightSampleProvider(final GBDevice device, final DaoSession session) { return new MiScaleSampleProvider(device, session); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/MiCompositionScaleDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/MiCompositionScaleDeviceSupport.java index 1e4548a0c..2525fbaef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/MiCompositionScaleDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/MiCompositionScaleDeviceSupport.java @@ -20,17 +20,26 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miscale; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Intent; +import android.os.Parcelable; 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.List; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.miscale.MiScaleSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.MiScaleWeightSample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; @@ -38,26 +47,19 @@ 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.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class MiCompositionScaleDeviceSupport extends AbstractBTLEDeviceSupport { - private static final Logger LOG = LoggerFactory.getLogger(MiCompositionScaleDeviceSupport.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 MiCompositionScaleDeviceSupport() { super(LOG); @@ -68,12 +70,20 @@ public class MiCompositionScaleDeviceSupport extends AbstractBTLEDeviceSupport { addSupportedService(UUID.fromString("00001530-0000-3512-2118-0009af100700")); deviceInfoProfile = new DeviceInfoProfile<>(this); + final IntentListener mListener = intent -> { + if (DeviceInfoProfile.ACTION_DEVICE_INFO.equals(intent.getAction())) { + final Parcelable deviceInfo = intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO); + if (deviceInfo != null) { + handleDeviceInfo((DeviceInfo) deviceInfo); + } + } + }; deviceInfoProfile.addListener(mListener); addSupportedProfile(deviceInfoProfile); } @Override - protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + protected TransactionBuilder initializeDevice(final TransactionBuilder builder) { builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); LOG.debug("Requesting Device Info!"); @@ -88,38 +98,34 @@ public class MiCompositionScaleDeviceSupport extends AbstractBTLEDeviceSupport { } @Override - public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - super.onCharacteristicChanged(gatt, characteristic); + public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } - UUID characteristicUUID = characteristic.getUuid(); + final 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; - } + final byte flags = data[1]; + final boolean stabilized = testBit(flags, 5) && !testBit(flags, 7); 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(); + final int year = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 2); + final int month = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 4); + final int day = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 5); + final int hour = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 6); + final int minute = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 7); + final int second = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 8); + final 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); + final Date date = c.getTime(); + + float weightKg = WeightMeasurement.weightToKg( + characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 11), + flags + ); + handleWeightInfo(date, weightKg); } return true; @@ -128,35 +134,38 @@ public class MiCompositionScaleDeviceSupport extends AbstractBTLEDeviceSupport { return false; } - private boolean testBit(byte value, int offset) { + private boolean testBit(final byte value, final int offset) { return ((value >> offset) & 1) == 1; } - private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { - LOG.warn("Device info: " + info); + private void handleDeviceInfo(final DeviceInfo info) { + LOG.debug("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); + private void handleWeightInfo(final Date date, final float weightKg) { + GB.toast(getContext().getString(R.string.weight_kg, weightKg), Toast.LENGTH_SHORT, GB.INFO); + + try (DBHandler db = GBApplication.acquireDB()) { + final MiScaleSampleProvider provider = new MiScaleSampleProvider(getDevice(), db.getDaoSession()); + final Long userId = DBHelper.getUser(db.getDaoSession()).getId(); + final Long deviceId = DBHelper.getDevice(getDevice(), db.getDaoSession()).getId(); + + provider.addSample(new MiScaleWeightSample( + date.getTime(), + deviceId, + userId, + weightKg + )); + } catch (final Exception e) { + LOG.error("Error saving weight sample", e); + } } @Override public boolean useAutoConnect() { return false; } - - @Override - public boolean getImplicitCallbackModify() { - return true; - } - - @Override - public boolean getSendWriteRequestResponse() { - return false; - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/WeightMeasurement.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/WeightMeasurement.java index e590c72b4..237a89790 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/WeightMeasurement.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale/WeightMeasurement.java @@ -69,7 +69,7 @@ public class WeightMeasurement { return new WeightMeasurement(calendar.getTime(), weightKg); } - private static float weightToKg(float weight, byte flags) { + public static float weightToKg(float weight, byte flags) { boolean isLbs = testBit(flags, 0); boolean isJin = testBit(flags, 4);