From 0fecdf0e18b4ce764b71c249e02a86bc34236826 Mon Sep 17 00:00:00 2001 From: Daniel Dakhno Date: Sat, 10 Oct 2020 16:45:34 +0200 Subject: [PATCH] UM25: added base device support for UM25C voltage meter --- app/src/main/AndroidManifest.xml | 3 + .../devices/um25/Activity/DataActivity.java | 90 +++++++++ .../um25/Coordinator/UM25Coordinator.java | 143 ++++++++++++++ .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 4 + .../devices/um25/Data/CaptureGroup.java | 39 ++++ .../devices/um25/Data/MeasurementData.java | 87 +++++++++ .../devices/um25/Support/UM25BaseSupport.java | 179 ++++++++++++++++++ .../devices/um25/Support/UM25Support.java | 157 +++++++++++++++ .../gadgetbridge/util/DeviceHelper.java | 2 + .../main/res/layout/activity_um25_data.xml | 40 ++++ app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 1 + 13 files changed, 747 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Coordinator/UM25Coordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/CaptureGroup.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/MeasurementData.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25BaseSupport.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25Support.java create mode 100644 app/src/main/res/layout/activity_um25_data.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 692658903..a418c2258 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -613,5 +613,8 @@ android:name=".devices.qhybrid.CalibrationActivity" android:label="@string/qhybrid_title_calibration" android:parentActivityName=".devices.qhybrid.HRConfigActivity" /> + \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java new file mode 100644 index 000000000..313ac34d1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Activity/DataActivity.java @@ -0,0 +1,90 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.um25.Activity; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.widget.TextView; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import java.lang.reflect.Field; +import java.util.HashMap; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity; +import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Data.MeasurementData; +import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support.UM25Support; + +public class DataActivity extends AbstractGBActivity { + private HashMap valueViews = new HashMap<>(ValueDisplay.values().length); + + private enum ValueDisplay{ + VOLTAGE("voltage", "%.3fV", R.id.um25_text_voltage, 1000), + CURRENT("current", "%.4fA", R.id.um25_text_current, 1000), + WATTAGE("wattage", "%.4fW", R.id.um25_text_wattage, 1000), + ; + + private String variableName; + private String formatString; + private int textViewResource; + private float divisor; + + ValueDisplay(String variableName, String formatString, int textViewResource, float divisor) { + this.variableName = variableName; + this.formatString = formatString; + this.textViewResource = textViewResource; + this.divisor = divisor; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_um25_data); + } + + @Override + protected void onResume() { + super.onResume(); + LocalBroadcastManager.getInstance(this) + .registerReceiver( + measurementReceiver, + new IntentFilter(UM25Support.ACTION_MEASUREMENT_TAKEN) + ); + } + + @Override + protected void onPause() { + super.onPause(); + LocalBroadcastManager.getInstance(this) + .unregisterReceiver(measurementReceiver); + } + + private void displayMeasurementData(MeasurementData data){ + for(ValueDisplay display : ValueDisplay.values()){ + try { + TextView textView = valueViews.get(display.textViewResource); + if(textView == null){ + valueViews.put(display.textViewResource, textView = findViewById(display.textViewResource)); + } + Field field = data.getClass().getDeclaredField(display.variableName); + field.setAccessible(true); + float value = ((int) field.get(data)) / display.divisor; + String result = String.format(display.formatString, value); + textView.setText(result); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + private BroadcastReceiver measurementReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + MeasurementData data = (MeasurementData) intent.getSerializableExtra(UM25Support.EXTRA_KEY_MEASUREMENT_DATA); + displayMeasurementData(data); + } + }; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Coordinator/UM25Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Coordinator/UM25Coordinator.java new file mode 100644 index 000000000..8448d2f62 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/um25/Coordinator/UM25Coordinator.java @@ -0,0 +1,143 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator; + +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 java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import cyanogenmod.app.CustomTile; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.um25.Activity.DataActivity; +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.devices.um25.Support.UM25Support; + +public class UM25Coordinator extends AbstractDeviceCoordinator { + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + + } + + @NonNull + @Override + public Collection createBLEScanFilters() { + return Collections.singletonList( + new ScanFilter.Builder() + .setServiceUuid(ParcelUuid.fromString(UM25Support.UUID_SERVICE)) + .build() + ); + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + if(!"UM25C".equals(candidate.getName())) return DeviceType.UNKNOWN; + for(ParcelUuid service : candidate.getServiceUuids()){ + if(service.getUuid().toString().equals(UM25Support.UUID_SERVICE)) return DeviceType.UM25; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.UM25; + } + + @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 boolean supportsFindDevice() { + return false; + } + + @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 "Ruideng"; + } + + @Override + public boolean supportsAppsManagement() { + return true; + } + + @Override + public Class getAppsManagementActivity() { + return DataActivity.class; + } + + @Override + public boolean supportsCalendarEvents() { + return false; + } + + @Override + public boolean supportsRealtimeData() { + return false; + } + + @Override + public boolean supportsWeather() { + 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 44d1f1fdb..23f4edfa1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -94,6 +94,7 @@ public enum DeviceType { SONY_SWR12(310, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_sonyswr12), LIVEVIEW(320, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_liveview), WASPOS(330, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_waspos), + UM25(350, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_um25), 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/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index ae3d2679f..dfebe5d44 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -83,6 +83,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSuppo import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.tlw64.TLW64Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support.UM25Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.waspos.WaspOSDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport; @@ -334,6 +335,9 @@ public class DeviceSupportFactory { case WASPOS: deviceSupport = new ServiceDeviceSupport(new WaspOSDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case UM25: + deviceSupport = new ServiceDeviceSupport(new UM25Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/CaptureGroup.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/CaptureGroup.java new file mode 100644 index 000000000..79efac819 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/CaptureGroup.java @@ -0,0 +1,39 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Data; + +import java.io.Serializable; + +public class CaptureGroup implements Serializable { + private int index; + private int flownCurrent; + private int flownWattage; + + public CaptureGroup(int index, int flownCurrent, int flownWattage) { + this.flownCurrent = flownCurrent; + this.flownWattage = flownWattage; + this.index = index; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getFlownCurrent() { + return flownCurrent; + } + + public void setFlownCurrent(int flownCurrent) { + this.flownCurrent = flownCurrent; + } + + public int getFlownWattage() { + return flownWattage; + } + + public void setFlownWattage(int flownWattage) { + this.flownWattage = flownWattage; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/MeasurementData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/MeasurementData.java new file mode 100644 index 000000000..f108b7d35 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Data/MeasurementData.java @@ -0,0 +1,87 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Data; + +import java.io.Serializable; + +public class MeasurementData implements Serializable { + private int voltage; // voltage in millivolts + private int current; // current in milliampere + private int wattage; // wattage in milliwatt + private int temperatureCelcius; + private int temperatureFahreheit; + private CaptureGroup[] captureGroups; + private int voltageDataPositive; + private int voltageDataNegative; + private int chargedCurrent; // charged current in milliAmpereHours + private int chargedWattage; // charged current in milliWattHours + private int thresholdCurrent; // threshold current for charging detection + private int chargingSeconds; + private int cableResistance; // cable resistance in ohms + + public MeasurementData(int voltage, int current, int wattage, int temperatureCelcius, int temperatureFahreheit, CaptureGroup[] captureGroups, int voltageDataPositive, int voltageDataNegative, int chargedCurrent, int chargedWattage, int thresholdCurrent, int chargingSeconds, int cableResistance) { + this.voltage = voltage; + this.current = current; + this.wattage = wattage; + this.temperatureCelcius = temperatureCelcius; + this.temperatureFahreheit = temperatureFahreheit; + this.captureGroups = captureGroups; + this.voltageDataPositive = voltageDataPositive; + this.voltageDataNegative = voltageDataNegative; + this.chargedCurrent = chargedCurrent; + this.chargedWattage = chargedWattage; + this.thresholdCurrent = thresholdCurrent; + this.chargingSeconds = chargingSeconds; + this.cableResistance = cableResistance; + } + + public int getVoltage() { + return voltage; + } + + public int getCurrent() { + return current; + } + + public int getWattage() { + return wattage; + } + + public int getTemperatureCelcius() { + return temperatureCelcius; + } + + public int getTemperatureFahreheit() { + return temperatureFahreheit; + } + + public CaptureGroup[] getCaptureGroups() { + return captureGroups; + } + + public int getVoltageDataPositive() { + return voltageDataPositive; + } + + public int getVoltageDataNegative() { + return voltageDataNegative; + } + + public int getChargedCurrent() { + return chargedCurrent; + } + + public int getChargedWattage() { + return chargedWattage; + } + + public int getThresholdCurrent() { + return thresholdCurrent; + } + + public int getChargingSeconds() { + return chargingSeconds; + } + + public int getCableResistance() { + return cableResistance; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25BaseSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25BaseSupport.java new file mode 100644 index 000000000..be1bda6a3 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25BaseSupport.java @@ -0,0 +1,179 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support; + +import android.net.Uri; + +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.UUID; + +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; + +public class UM25BaseSupport extends AbstractBTLEDeviceSupport { + public UM25BaseSupport(Logger logger) { + super(logger); + } + + @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 onReadConfiguration(String config) { + + } + + @Override + public void onTestNewFunction() { + + } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25Support.java new file mode 100644 index 000000000..501d8e551 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/um25/Support/UM25Support.java @@ -0,0 +1,157 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Intent; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.UUID; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Data.CaptureGroup; +import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Data.MeasurementData; +import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; + +public class UM25Support extends UM25BaseSupport { + public static final String UUID_SERVICE = "0000ffe0-0000-1000-8000-00805f9b34fb"; + public static final String UUID_CHAR = "0000ffe1-0000-1000-8000-00805f9b34fb"; + + public static final String ACTION_MEASUREMENT_TAKEN = "com.nodomain.gadgetbridge.um25.MEASUREMENT_TAKEN"; + public static final String EXTRA_KEY_MEASUREMENT_DATA = "EXTRA_MEASUREMENT_DATA"; + public static final int LOOP_DELAY = 500; + + private final byte[] COMMAND_UPDATE = new byte[]{(byte) 0xF0}; + private final int PAYLOAD_LENGTH = 130; + + private ByteBuffer buffer = ByteBuffer.allocate(PAYLOAD_LENGTH); + + private static final Logger logger = LoggerFactory.getLogger(UM25Support.class); + + + public UM25Support() { + super(logger); + addSupportedService(UUID.fromString(UUID_SERVICE)); + this.buffer.mark(); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + return builder + .add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())) + .notify(getCharacteristic(UUID.fromString(UUID_CHAR)), true) + .add(new BtLEAction(null) { + @Override + public boolean expectsResult() { + return false; + } + + @Override + public boolean run(BluetoothGatt gatt) { + logger.debug("initialized, starting timers"); + startLoop(); + return true; + } + }) + .add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); + } + + private void startLoop(){ + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); + executor.scheduleWithFixedDelay(this::sendReadCommand, 0, LOOP_DELAY, TimeUnit.MILLISECONDS); + } + + private void sendReadCommand(){ + logger.debug("sending read command"); + buffer.reset(); + new TransactionBuilder("send read command") + .write(getCharacteristic(UUID.fromString(UUID_CHAR)), COMMAND_UPDATE) + .queue(getQueue()); + logger.debug("sent command"); + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + if(!characteristic.getUuid().toString().equals(UUID_CHAR)) return false; + + try{ + buffer.put(characteristic.getValue()); + + if(buffer.position() == PAYLOAD_LENGTH){ + handlePayload(buffer); + } + }catch (BufferOverflowException e){ + logger.error("buffer overflow"); + } + + return true; + } + + private void handlePayload(ByteBuffer payload){ + String payloadString = StringUtils.bytesToHex(payload.array()); + payloadString = payloadString.replaceAll("(..)", "$1 "); + logger.debug("payload: " + payloadString); + payload.order(ByteOrder.BIG_ENDIAN); + int voltage = payload.getShort(2); + int current = payload.getShort(4); + int wattage = payload.getShort(8); + int temperatureCelsius = payload.getShort(10); + int temperatureFahrenheit = payload.getShort(12); + + final int STORAGE_START = 16; + + CaptureGroup[] groups = new CaptureGroup[10]; + + for(int i = 0; i < 10; i++){ + groups[i] = new CaptureGroup( + i, + payload.getInt(STORAGE_START + i * 4 + 0), + payload.getInt(STORAGE_START + i * 4 + 4) + ); + } + + int voltagePositive = payload.getShort(96); + int voltageNegative = payload.getShort(98); + int chargedCurrent = payload.getInt(102); + int chargedWattage = payload.getInt(106); + int thresholdCurrent = payload.get(111); + int chargingSeconds = payload.getInt(112); + int cableResistance = payload.getInt(122); + + logger.debug("variable: " + chargedCurrent); + + MeasurementData data = new MeasurementData( + voltage, + current, + wattage, + temperatureCelsius, + temperatureFahrenheit, + groups, + voltagePositive, + voltageNegative, + chargedCurrent, + chargedWattage, + thresholdCurrent, + chargingSeconds, + cableResistance + ); + + Intent measurementIntent = new Intent(ACTION_MEASUREMENT_TAKEN); + + measurementIntent.putExtra(EXTRA_KEY_MEASUREMENT_DATA, data); + + LocalBroadcastManager.getInstance(getContext()) + .sendBroadcast(measurementIntent); + } +} 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 7f6580ed9..68380f548 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -101,6 +101,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator; @@ -294,6 +295,7 @@ public class DeviceHelper { result.add(new LefunDeviceCoordinator()); result.add(new SonySWR12DeviceCoordinator()); result.add(new WaspOSCoordinator()); + result.add(new UM25Coordinator()); return result; } diff --git a/app/src/main/res/layout/activity_um25_data.xml b/app/src/main/res/layout/activity_um25_data.xml new file mode 100644 index 000000000..b39781991 --- /dev/null +++ b/app/src/main/res/layout/activity_um25_data.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 2dac8429e..d55488a41 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -14,4 +14,5 @@ http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout 160dp 16dp 20dp + 60dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 585c3f55b..c6b4d6982 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -824,6 +824,7 @@ Amazfit X Zepp E Vibratissimo + UM-25 LiveView HPlus Makibes F68