Zepp OS: Add temperature support

This commit is contained in:
José Rebelo
2025-03-23 18:33:10 +00:00
parent c33b0ffcbb
commit b95f868bff
18 changed files with 91 additions and 35 deletions
@@ -113,7 +113,7 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
if (!coordinator.supportsRealtimeData()) {
tabList.remove("livestats");
}
if (!coordinator.supportsTemperatureMeasurement()) {
if (!coordinator.supportsTemperatureMeasurement(device)) {
tabList.remove("temperature");
}
if (!coordinator.supportsCyclingData()) {
@@ -190,7 +190,7 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
case "spo2":
return new Spo2ChartFragment();
case "temperature":
return coordinator.supportsContinuousTemperature()? new TemperatureDailyFragment(): new TemperatureChartFragment();
return coordinator.supportsContinuousTemperature(getDevice())? new TemperatureDailyFragment(): new TemperatureChartFragment();
case "cycling":
return new CyclingChartFragment();
case "weight":
@@ -550,12 +550,12 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return false;
}
@Override
public boolean supportsContinuousTemperature() {
public boolean supportsContinuousTemperature(final GBDevice device) {
return false;
}
@@ -257,13 +257,13 @@ public interface DeviceCoordinator {
* Returns true if measurement and fetching of body temperature is supported by the device
* (with this coordinator).
*/
boolean supportsTemperatureMeasurement();
boolean supportsTemperatureMeasurement(GBDevice device);
/**
* Returns true if continuous temperature measurement used in device
* (with this coordinator).
*/
boolean supportsContinuousTemperature();
boolean supportsContinuousTemperature(GBDevice device);
/**
* Returns true if SpO2 measurement and fetching is supported by the device
@@ -235,7 +235,7 @@ public abstract class AbstractColmiR0xCoordinator extends AbstractBLEDeviceCoord
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
final List<Integer> health = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH);
health.add(R.xml.devicesettings_colmi_r0x);
if (supportsContinuousTemperature()) {
if (supportsContinuousTemperature(device)) {
health.add(R.xml.devicesettings_temperature_automatic_enable);
}
return deviceSpecificSettings;
@@ -43,12 +43,12 @@ public class ColmiR09Coordinator extends AbstractColmiR0xCoordinator {
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return true;
}
@Override
public boolean supportsContinuousTemperature() {
public boolean supportsContinuousTemperature(final GBDevice device) {
return true;
}
}
@@ -101,7 +101,7 @@ public class FemometerVinca2DeviceCoordinator extends AbstractDeviceCoordinator
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return true;
}
@@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpec
import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.GenericHrvValueSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.GenericTemperatureSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay;
@@ -60,6 +61,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.GenericHrvValueSampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.GenericTemperatureSampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.HuamiHeartRateManualSampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.HuamiHeartRateMaxSampleDao;
@@ -112,6 +114,7 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
put(session.getHuamiPaiSampleDao(), HuamiPaiSampleDao.Properties.DeviceId);
put(session.getHuamiSleepRespiratoryRateSampleDao(), HuamiSleepRespiratoryRateSampleDao.Properties.DeviceId);
put(session.getGenericHrvValueSampleDao(), GenericHrvValueSampleDao.Properties.DeviceId);
put(session.getGenericTemperatureSampleDao(), GenericTemperatureSampleDao.Properties.DeviceId);
put(session.getHuamiSleepSessionSampleDao(), HuamiSleepSessionSampleDao.Properties.DeviceId);
put(session.getBaseActivitySummaryDao(), BaseActivitySummaryDao.Properties.DeviceId);
}};
@@ -213,6 +216,11 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
return new GenericHrvValueSampleProvider(device, session);
}
@Override
public GenericTemperatureSampleProvider getTemperatureSampleProvider(GBDevice device, DaoSession session) {
return new GenericTemperatureSampleProvider(device, session);
}
@Override
public TimeSampleProvider<? extends SleepScoreSample> getSleepScoreProvider(final GBDevice device, final DaoSession session) {
return new HuamiSleepSessionSampleProvider(device, session);
@@ -523,8 +523,13 @@ public abstract class ZeppOsCoordinator extends HuamiCoordinator {
}
@Override
public boolean supportsTemperatureMeasurement() {
return false; // TODO: supportsDisplayItem(device, "temperature");
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return supportsDisplayItem(device, "thermometer");
}
@Override
public boolean supportsContinuousTemperature(final GBDevice device) {
return supportsDisplayItem(device, "thermometer");
}
public boolean supportsAgpsUpdates() {
@@ -205,12 +205,12 @@ public abstract class HuaweiBRCoordinator extends AbstractBLClassicDeviceCoordin
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return huaweiCoordinator.supportsTemperature();
}
@Override
public boolean supportsContinuousTemperature() {
public boolean supportsContinuousTemperature(final GBDevice device) {
return huaweiCoordinator.supportsTemperature();
}
@@ -41,7 +41,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
@@ -214,12 +213,12 @@ public abstract class HuaweiLECoordinator extends AbstractBLEDeviceCoordinator i
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return huaweiCoordinator.supportsTemperature();
}
@Override
public boolean supportsContinuousTemperature() {
public boolean supportsContinuousTemperature(final GBDevice device) {
return huaweiCoordinator.supportsTemperature();
}
@@ -84,12 +84,12 @@ public abstract class AbstractMijiaLywsdCoordinator extends AbstractBLEDeviceCoo
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return true;
}
@Override
public boolean supportsContinuousTemperature() {
public boolean supportsContinuousTemperature(final GBDevice device) {
return true;
}
@@ -145,7 +145,7 @@ public class TestDeviceCoordinator extends AbstractDeviceCoordinator {
@Override
public TimeSampleProvider<? extends TemperatureSample> getTemperatureSampleProvider(final GBDevice device, final DaoSession session) {
return supportsTemperatureMeasurement() ? new TestTemperatureSampleProvider() : super.getTemperatureSampleProvider(device, session);
return supportsTemperatureMeasurement(device) ? new TestTemperatureSampleProvider() : super.getTemperatureSampleProvider(device, session);
}
@Override
@@ -325,7 +325,7 @@ public class TestDeviceCoordinator extends AbstractDeviceCoordinator {
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return supports(getTestDevice(), TestFeature.TEMPERATURE_MEASUREMENT);
}
@@ -172,7 +172,7 @@ public class UltrahumanDeviceCoordinator extends AbstractBLEDeviceCoordinator {
}
@Override
public boolean supportsContinuousTemperature() {
public boolean supportsContinuousTemperature(final GBDevice device) {
return true;
}
@@ -208,7 +208,7 @@ public class UltrahumanDeviceCoordinator extends AbstractBLEDeviceCoordinator {
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return true;
}
@@ -20,6 +20,7 @@ import java.util.regex.Pattern;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public class XiaomiWatchS1ProCoordinator extends XiaomiCoordinator {
@@ -60,7 +61,7 @@ public class XiaomiWatchS1ProCoordinator extends XiaomiCoordinator {
}
@Override
public boolean supportsTemperatureMeasurement() {
public boolean supportsTemperatureMeasurement(final GBDevice device) {
return true;
}
}
@@ -314,7 +314,7 @@ public class ColmiR0xDeviceSupport extends AbstractBTLEDeviceSupport {
daysAgo++;
fetchHistoryHRV();
} else {
if (getDevice().getDeviceCoordinator().supportsTemperatureMeasurement()) {
if (getDevice().getDeviceCoordinator().supportsTemperatureMeasurement(gbDevice)) {
fetchTemperature();
} else {
fetchRecordedDataFinished();
@@ -1728,7 +1728,7 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
this.fetchOperationQueue.add(new FetchSleepRespiratoryRateOperation(this));
}
if ((dataTypes & RecordedDataTypes.TYPE_TEMPERATURE) != 0 && coordinator.supportsTemperatureMeasurement()) {
if ((dataTypes & RecordedDataTypes.TYPE_TEMPERATURE) != 0 && coordinator.supportsTemperatureMeasurement(gbDevice)) {
this.fetchOperationQueue.add(new FetchTemperatureOperation(this));
}
@@ -104,7 +104,7 @@ public abstract class AbstractRepeatingFetchOperation extends AbstractFetchOpera
return false;
}
if (fetchCount > 5) {
if (fetchCount > 10) {
LOG.warn("Already have {} fetch rounds for {}, not doing another one", fetchCount, getName());
return false;
}
@@ -16,15 +16,27 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.fetch;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.GenericTemperatureSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.GenericTemperatureSample;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
/**
@@ -51,17 +63,48 @@ public class FetchTemperatureOperation extends AbstractRepeatingFetchOperation {
final ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
while (buffer.position() < bytes.length) {
final int temp1 = buffer.getShort();
final int temp2 = buffer.getShort();
final int temp3 = buffer.getShort();
final int temp4 = buffer.getShort();
final List<GenericTemperatureSample> samples = new ArrayList<>();
// TODO persist / parse rest
LOG.warn("temp1: {}, temp2={}, temp3: {}, temp4={}", temp1, temp2, temp3, temp4);
while (buffer.position() < bytes.length) {
final int unk1 = buffer.getShort(); // 32767
final int temperature = buffer.getShort();
final int unk3 = buffer.getShort(); // 23130
final int unk4 = buffer.getShort(); // 23130
LOG.trace(
"Temperature at {}: {}",
DateTimeUtils.formatIso8601(timestamp.getTime()),
temperature
);
final GenericTemperatureSample sample = new GenericTemperatureSample();
sample.setTimestamp(timestamp.getTimeInMillis());
sample.setTemperature(temperature / 100f);
sample.setTemperatureType(0);
samples.add(sample);
timestamp.add(Calendar.MINUTE, 1);
}
return false;
timestamp.add(Calendar.MINUTE, -1);
return persistSamples(samples);
}
protected boolean persistSamples(final List<GenericTemperatureSample> samples) {
try (DBHandler handler = GBApplication.acquireDB()) {
final DaoSession session = handler.getDaoSession();
final HuamiCoordinator coordinator = (HuamiCoordinator) getDevice().getDeviceCoordinator();
final GenericTemperatureSampleProvider sampleProvider = coordinator.getTemperatureSampleProvider(getDevice(), session);
sampleProvider.persistForDevice(getContext(), getDevice(), samples);
} catch (final Exception e) {
GB.toast(getContext(), "Error saving temperature samples", Toast.LENGTH_LONG, GB.ERROR, e);
return false;
}
return true;
}
@Override