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 extends ScanFilter> 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 extends Activity> getPairingActivity() {
+ return null;
+ }
+
+ @Override
+ public boolean supportsActivityDataFetching() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsActivityTracking() {
+ return false;
+ }
+
+ @Override
+ public SampleProvider extends ActivitySample> 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 extends Activity> 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 extends Alarm> 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