mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-01 06:22:55 +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:name=".devices.qhybrid.CalibrationActivity"
|
||||||
android:label="@string/qhybrid_title_calibration"
|
android:label="@string/qhybrid_title_calibration"
|
||||||
android:parentActivityName=".devices.qhybrid.HRConfigActivity" />
|
android:parentActivityName=".devices.qhybrid.HRConfigActivity" />
|
||||||
|
<activity
|
||||||
|
android:name=".devices.um25.Activity.DataActivity"
|
||||||
|
android:exported="true" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</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),
|
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),
|
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),
|
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);
|
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
|
||||||
|
|
||||||
private final int key;
|
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.roidmi.RoidmiSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12DeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12DeviceSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.tlw64.TLW64Support;
|
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.vibratissimo.VibratissimoSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.waspos.WaspOSDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.waspos.WaspOSDeviceSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport;
|
||||||
@ -334,6 +335,9 @@ public class DeviceSupportFactory {
|
|||||||
case WASPOS:
|
case WASPOS:
|
||||||
deviceSupport = new ServiceDeviceSupport(new WaspOSDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
deviceSupport = new ServiceDeviceSupport(new WaspOSDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||||
break;
|
break;
|
||||||
|
case UM25:
|
||||||
|
deviceSupport = new ServiceDeviceSupport(new UM25Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (deviceSupport != null) {
|
if (deviceSupport != null) {
|
||||||
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);
|
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.roidmi.Roidmi3Coordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator;
|
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.vibratissimo.VibratissimoCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator;
|
||||||
@ -294,6 +295,7 @@ public class DeviceHelper {
|
|||||||
result.add(new LefunDeviceCoordinator());
|
result.add(new LefunDeviceCoordinator());
|
||||||
result.add(new SonySWR12DeviceCoordinator());
|
result.add(new SonySWR12DeviceCoordinator());
|
||||||
result.add(new WaspOSCoordinator());
|
result.add(new WaspOSCoordinator());
|
||||||
|
result.add(new UM25Coordinator());
|
||||||
|
|
||||||
return result;
|
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="nav_header_height">160dp</dimen>
|
||||||
<dimen name="fab_margin">16dp</dimen>
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
<dimen name="dialog_margin">20dp</dimen>
|
<dimen name="dialog_margin">20dp</dimen>
|
||||||
|
<dimen name="um25_value_text_size">60dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -824,6 +824,7 @@
|
|||||||
<string name="devicetype_amazfit_x">Amazfit X</string>
|
<string name="devicetype_amazfit_x">Amazfit X</string>
|
||||||
<string name="devicetype_zepp_e">Zepp E</string>
|
<string name="devicetype_zepp_e">Zepp E</string>
|
||||||
<string name="devicetype_vibratissimo">Vibratissimo</string>
|
<string name="devicetype_vibratissimo">Vibratissimo</string>
|
||||||
|
<string name="devicetype_um25">UM-25</string>
|
||||||
<string name="devicetype_liveview">LiveView</string>
|
<string name="devicetype_liveview">LiveView</string>
|
||||||
<string name="devicetype_hplus">HPlus</string>
|
<string name="devicetype_hplus">HPlus</string>
|
||||||
<string name="devicetype_makibes_f68">Makibes F68</string>
|
<string name="devicetype_makibes_f68">Makibes F68</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user