mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-24 19:06:53 +01:00
UM25: added base device support for UM25C voltage meter
This commit is contained in:
parent
627bf033c3
commit
0fecdf0e18
@ -613,5 +613,8 @@
|
||||
android:name=".devices.qhybrid.CalibrationActivity"
|
||||
android:label="@string/qhybrid_title_calibration"
|
||||
android:parentActivityName=".devices.qhybrid.HRConfigActivity" />
|
||||
<activity
|
||||
android:name=".devices.um25.Activity.DataActivity"
|
||||
android:exported="true" />
|
||||
</application>
|
||||
</manifest>
|
@ -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<Integer, TextView> 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);
|
||||
}
|
||||
};
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
40
app/src/main/res/layout/activity_um25_data.xml
Normal file
40
app/src/main/res/layout/activity_um25_data.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="-"
|
||||
android:id="@+id/um25_text_voltage"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="@dimen/um25_value_text_size"
|
||||
android:textColor="@android:color/holo_green_dark"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="-"
|
||||
android:id="@+id/um25_text_current"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="@dimen/um25_value_text_size"
|
||||
android:textColor="@android:color/holo_red_dark"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="-"
|
||||
android:id="@+id/um25_text_wattage"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="@dimen/um25_value_text_size"
|
||||
android:textColor="@android:color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -14,4 +14,5 @@ http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
|
||||
<dimen name="nav_header_height">160dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
<dimen name="dialog_margin">20dp</dimen>
|
||||
<dimen name="um25_value_text_size">60dp</dimen>
|
||||
</resources>
|
||||
|
@ -824,6 +824,7 @@
|
||||
<string name="devicetype_amazfit_x">Amazfit X</string>
|
||||
<string name="devicetype_zepp_e">Zepp E</string>
|
||||
<string name="devicetype_vibratissimo">Vibratissimo</string>
|
||||
<string name="devicetype_um25">UM-25</string>
|
||||
<string name="devicetype_liveview">LiveView</string>
|
||||
<string name="devicetype_hplus">HPlus</string>
|
||||
<string name="devicetype_makibes_f68">Makibes F68</string>
|
||||
|
Loading…
Reference in New Issue
Block a user