Huawei: refactoring dyct sync. Split to classes

This commit is contained in:
Me7c7
2026-01-08 19:30:54 +01:00
committed by José Rebelo
parent e47d1c82b8
commit 1417a85996
13 changed files with 643 additions and 294 deletions
@@ -1028,6 +1028,12 @@ public class HuaweiCoordinator {
return false;
}
public boolean supportsAltitude() {
if (supportsExpandCapability())
return supportsExpandCapability(72);
return false;
}
public boolean supportsArterialStiffnessDetection() {
if (supportsExpandCapability())
return supportsExpandCapability(75);
@@ -17,27 +17,10 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
public class HuaweiDictTypes {
public static final int BLOOD_PRESSURE_CLASS = 10002;
public static final int BODY_TEMPERATURE_CLASS = 400011;
public static final int SKIN_TEMPERATURE_CLASS = 400012;
public static final int SKIN_TEMPERATURE_VALUE = 400012430;
public static final int SLEEP_DETAILS_CLASS = 700013;
public static final int ARRHYTHMIA_CLASS = 700004;
public static final int HRV_CLASS = 500044;
public static final int HRV_RMSSD_VALUE = 500044831;
public static final int EMOTION_CLASS = 500031;
public static final int EMOTION_STATUS_VALUE = 500031195;
public static final int EMOTION_VALENCE_CHARACTER_VALUE = 500031347;
public static final int EMOTION_ORIGIN_STATUS_VALUE = 500031160;
public static final int EMOTION_AROUSAL_CHARACTER_VALUE = 500031842;
public static final int SLEEP_APNEA_CLASS = 500002;
public static final int SLEEP_APNEA_LEVEL_VALUE = 500002847;
}
@@ -43,8 +43,6 @@ import java.io.File;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -74,22 +72,17 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupp
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiDictTypes;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiEcgFileParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiEmotionsSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiGpsParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiHrvValueSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPdrParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSequenceDataFileParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSleepApneaSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSleepStageSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSleepStatsSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiStressParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiStressSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTemperatureSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTruSleepParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTrueSleepSequenceDataParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiUtil;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.CameraRemote;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Notifications;
@@ -104,13 +97,9 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEmotionsSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiHrvValueSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepApneaSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStageSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStatsSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiStressSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiTemperatureSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSample;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSampleDao;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSample;
@@ -149,7 +138,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NavigationInfoSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.datasync.HuaweiDataSyncNotificationPictures;
@@ -168,6 +156,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2P
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDirection;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PTrackService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncInterface;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.AcceptAgreementsRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetAppInfoParams;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetContactsCount;
@@ -1548,7 +1537,7 @@ public class HuaweiSupportProvider {
stepStart = 946684800;
// To avoid of downloading of very big file and OOM error set ECG start time to step start time.
if(ecgStart == 0)
if (ecgStart == 0)
ecgStart = stepStart;
}
final GetSleepDataCountRequest getSleepDataCountRequest;
@@ -1629,225 +1618,41 @@ public class HuaweiSupportProvider {
private void fetchActivityDataP2P() {
HuaweiP2PDataDictionarySyncService P2PSyncService = HuaweiP2PDataDictionarySyncService.getRegisteredInstance(huaweiP2PManager);
if (P2PSyncService != null) {
List<Integer> list = P2PSyncService.checkSupported(this.getHuaweiCoordinator(), Arrays.asList(
HuaweiDictTypes.SKIN_TEMPERATURE_CLASS,
HuaweiDictTypes.HRV_CLASS,
HuaweiDictTypes.EMOTION_CLASS,
HuaweiDictTypes.SLEEP_APNEA_CLASS));
if (!list.isEmpty()) {
syncState.setP2pSync(true);
P2PSyncService.startSync(list, new HuaweiP2PDataDictionarySyncService.DictionarySyncCallback() {
@Override
public long onGetLastDataSyncTimestamp(int dictClass) {
LOG.info("DictionarySyncCallback onGetLastDataDictLastTimestamp: {}", dictClass);
if (dictClass == HuaweiDictTypes.SKIN_TEMPERATURE_CLASS) {
try (DBHandler db = GBApplication.acquireDB()) {
HuaweiTemperatureSampleProvider sleepStatsSampleProvider = new HuaweiTemperatureSampleProvider(gbDevice, db.getDaoSession());
return sleepStatsSampleProvider.getLastFetchTimestamp();
} catch (Exception e) {
LOG.warn("Exception for getting skin temperature start time", e);
}
return 0;
} else if (dictClass == HuaweiDictTypes.HRV_CLASS) {
try (DBHandler db = GBApplication.acquireDB()) {
HuaweiHrvValueSampleProvider hrvStatsSampleProvider = new HuaweiHrvValueSampleProvider(gbDevice, db.getDaoSession());
return hrvStatsSampleProvider.getLastFetchTimestamp();
} catch (Exception e) {
LOG.warn("Exception for getting HRV start time", e);
}
return 0;
} else if (dictClass == HuaweiDictTypes.EMOTION_CLASS) {
try (DBHandler db = GBApplication.acquireDB()) {
HuaweiEmotionsSampleProvider emotionsStatsSampleProvider = new HuaweiEmotionsSampleProvider(gbDevice, db.getDaoSession());
return emotionsStatsSampleProvider.getLastFetchTimestamp();
} catch (Exception e) {
LOG.warn("Exception for getting emotion start time", e);
}
return 0;
} else if (dictClass == HuaweiDictTypes.SLEEP_APNEA_CLASS) {
return 0;
}
return -1;
}
@Override
public void onData(int dictClass, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
LOG.info("DictionarySyncCallback onData: {}", dictClass);
if (dictClass == HuaweiDictTypes.SKIN_TEMPERATURE_CLASS) {
List<HuaweiTemperatureSample> temperatureSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Float temperature = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() != 10) {
LOG.info("skin temperature unexpected tag: {}", val.getTag());
continue;
}
if (val.getDataType() == HuaweiDictTypes.SKIN_TEMPERATURE_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 20 && value <= 42) {
temperature = (float) value;
}
} else {
LOG.info("skin temperature unknown data type: {}", val.getDataType());
}
}
if(temperature != null) {
HuaweiTemperatureSample sample = new HuaweiTemperatureSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setTemperature(temperature);
sample.setTemperatureType(TemperatureSample.TYPE_SKIN);
sample.setTemperatureLocation(TemperatureSample.LOCATION_WRIST);
temperatureSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiTemperatureSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, temperatureSamples);
} catch (Exception e) {
LOG.error("Cannot save skin temperature samples, continue");
}
} else if (dictClass == HuaweiDictTypes.HRV_CLASS) {
List<HuaweiHrvValueSample> hrvSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Integer rmssd = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() != 10) {
LOG.info("HRV unexpected tag: {}", val.getTag());
continue;
}
if (val.getDataType() == HuaweiDictTypes.HRV_RMSSD_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value <= 200) {
rmssd = (int) value;
}
} else {
LOG.info("HRV unknown data type: {}", val.getDataType());
}
}
if(rmssd != null) {
HuaweiHrvValueSample sample = new HuaweiHrvValueSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setValue(rmssd);
hrvSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiHrvValueSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, hrvSamples);
} catch (Exception e) {
LOG.error("Cannot save HRV samples, continue");
}
} else if (dictClass == HuaweiDictTypes.EMOTION_CLASS) {
List<HuaweiEmotionsSample> emotionsSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Integer status = null;
Double valenceCharacter = null;
Integer originStatus = null;
Double arousalCharacter = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() != 10) {
LOG.info("emotions unexpected tag: {}", val.getTag());
continue;
}
if (val.getDataType() == HuaweiDictTypes.EMOTION_STATUS_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value < 100) {
status = (int) value;
}
} else if (val.getDataType() == HuaweiDictTypes.EMOTION_VALENCE_CHARACTER_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value <= 100) {
valenceCharacter = value;
}
} else if (val.getDataType() == HuaweiDictTypes.EMOTION_ORIGIN_STATUS_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value < 100) {
originStatus = (int) value;
}
} else if (val.getDataType() == HuaweiDictTypes.EMOTION_AROUSAL_CHARACTER_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value <= 100) {
arousalCharacter = value;
}
} else {
LOG.info("emotions unknown data type: {}", val.getDataType());
}
}
if (status != null || valenceCharacter != null || originStatus != null || arousalCharacter != null) {
HuaweiEmotionsSample sample = new HuaweiEmotionsSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setStatus(status == null ? 0 : status);
sample.setValenceCharacter(valenceCharacter);
sample.setOriginStatus(originStatus);
sample.setArousalCharacter(arousalCharacter);
emotionsSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiEmotionsSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, emotionsSamples);
} catch (Exception e) {
LOG.error("Cannot save emotions samples, continue");
}
} else if (dictClass == HuaweiDictTypes.SLEEP_APNEA_CLASS) {
List<HuaweiSleepApneaSample> sleepApneaSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Integer level = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() == 10) {
if (val.getDataType() == HuaweiDictTypes.SLEEP_APNEA_LEVEL_VALUE) {
int value = (int) HuaweiUtil.convBytes2Double(val.getValue());
if (value == 1 || value == 2 || value == 3 || value == 4) {
level = value;
} else {
LOG.info("sleep apnea invalid value: {}", value);
}
} else {
LOG.info("sleep apnea unknown data type: {}", val.getDataType());
}
} else {
LOG.info("sleep apnea unsupported tag: {}", val.getTag());
}
}
if(level != null) {
LOG.info("APNEA timestamp: {} lastTime: {} level: {}", new Date(timestamp), lastTime, level);
HuaweiSleepApneaSample sample = new HuaweiSleepApneaSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setLevel(level);
sleepApneaSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiSleepApneaSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, sleepApneaSamples);
} catch (Exception e) {
LOG.error("Cannot save sleep apnea samples, continue");
}
}
}
@Override
public void onComplete(boolean complete) {
LOG.info("Sync P2P Data complete");
syncState.setP2pSync(false);
}
});
}
if (P2PSyncService == null) {
return;
}
Map<Integer, HuaweiDictionarySyncInterface> sup = P2PSyncService.getAllSupported(this.getHuaweiCoordinator());
if (sup.isEmpty()) {
return;
}
syncState.setP2pSync(true);
List<Integer> dictClasses = new ArrayList<>(sup.keySet());
P2PSyncService.startSync(dictClasses, new HuaweiP2PDataDictionarySyncService.DictionarySyncCallback() {
@Override
public long onGetLastDataSyncTimestamp(int dictClass) {
LOG.info("DictionarySyncCallback onGetLastDataDictLastTimestamp: {}", dictClass);
HuaweiDictionarySyncInterface cur = sup.get(dictClass);
if(cur == null) {
return -1;
}
return cur.getLastDataSyncTimestamp(gbDevice);
}
@Override
public void onData(int dictClass, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
LOG.info("DictionarySyncCallback onData: {}", dictClass);
HuaweiDictionarySyncInterface cur = sup.get(dictClass);
if(cur != null) {
cur.handleData(context, gbDevice, dictData);
}
}
@Override
public void onComplete(boolean complete) {
LOG.info("Sync P2P Data complete");
syncState.setP2pSync(false);
}
});
}
private void fetchWorkoutData() {
@@ -2036,15 +1841,15 @@ public class HuaweiSupportProvider {
} else if (
callSpec.command == CallSpec.CALL_ACCEPT ||
callSpec.command == CallSpec.CALL_START) {
byte type = getHuaweiCoordinator().supportsNotificationsStartCall()?Notifications.NotificationType.startCall:Notifications.NotificationType.stopNotification;
byte type = getHuaweiCoordinator().supportsNotificationsStartCall() ? Notifications.NotificationType.startCall : Notifications.NotificationType.stopNotification;
StopNotificationRequest stopNotificationRequest = new StopNotificationRequest(this, type);
try {
stopNotificationRequest.doPerform();
} catch (IOException e) {
LOG.error("Failed to send stop call notification", e);
}
}else if (
callSpec.command == CallSpec.CALL_REJECT ||
} else if (
callSpec.command == CallSpec.CALL_REJECT ||
callSpec.command == CallSpec.CALL_END
) {
StopNotificationRequest stopNotificationRequest = new StopNotificationRequest(this, Notifications.NotificationType.stopNotification);
@@ -2189,10 +1994,10 @@ public class HuaweiSupportProvider {
.buildDelete();
tableDeleteQuery.executeDeleteWithoutDetachingEntities();
int sampleRate = (int) (data.getEcgData().size() / ((data.getEndTime() - data.getStartTime())/ 1000));
int sampleRate = (int) (data.getEcgData().size() / ((data.getEndTime() - data.getStartTime()) / 1000));
int delta = 0;
List<HuaweiEcgDataSample> res = new ArrayList<>();
for (Float d: data.getEcgData()) {
for (Float d : data.getEcgData()) {
HuaweiEcgDataSample dataSample = new HuaweiEcgDataSample(summarySample.getEcgId(), delta, d);
res.add(dataSample);
delta += sampleRate;
@@ -3306,8 +3111,6 @@ public class HuaweiSupportProvider {
}
public boolean downloadTruSleepData(int start, int end) {
// We only get the data if TruSleep is supported
if (!getHuaweiCoordinator().supportsTruSleep())
return false;
@@ -3427,7 +3230,7 @@ public class HuaweiSupportProvider {
}
LOG.info("ECG result: {}", results);
if (results != null && !results.getEcgDataList().isEmpty()) {
for(HuaweiEcgFileParser.EcgData dt: results.getEcgDataList()) {
for (HuaweiEcgFileParser.EcgData dt : results.getEcgDataList()) {
addEcgData(dt);
}
}
@@ -3673,7 +3476,7 @@ public class HuaweiSupportProvider {
public void onSetNavigationInfo(NavigationInfoSpec navigationInfoSpec) {
LOG.info("navigation: {}", navigationInfoSpec);
HuaweiP2PDirection nav = HuaweiP2PDirection.getRegisteredInstance(huaweiP2PManager);
if(nav != null) {
if (nav != null) {
nav.updateInstruction(navigationInfoSpec.distanceToTurn, HuaweiP2PDirection.actionToIconId(navigationInfoSpec.nextAction), navigationInfoSpec.instruction);
}
}
@@ -29,10 +29,18 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiDictTypes;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiP2PManager;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncAltitude;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncArterialStiffness;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncBloodPressure;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncEmotion;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncHRV;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncInterface;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncLakeLouiseAMS;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncSkinTemperature;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync.HuaweiDictionarySyncSleepApnea;
public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
private final Logger LOG = LoggerFactory.getLogger(HuaweiP2PDataDictionarySyncService.class);
@@ -46,7 +54,9 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
public interface DictionarySyncCallback {
long onGetLastDataSyncTimestamp(int dictClass);
void onData(int dictClass, List<HuaweiP2PDataDictionarySyncService.DictData> dictData);
void onComplete(boolean complete);
}
@@ -79,33 +89,38 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
(byte) value};
}
public List<Integer> checkSupported(HuaweiCoordinator coordinator, List<Integer> list) {
List<Integer> result = new ArrayList<>();
private List<HuaweiDictionarySyncInterface> getSyncHandlers() {
List<HuaweiDictionarySyncInterface> ret = new ArrayList<>();
ret.add(new HuaweiDictionarySyncSkinTemperature());
ret.add(new HuaweiDictionarySyncHRV());
ret.add(new HuaweiDictionarySyncEmotion());
ret.add(new HuaweiDictionarySyncSleepApnea());
ret.add(new HuaweiDictionarySyncArterialStiffness());
ret.add(new HuaweiDictionarySyncAltitude());
ret.add(new HuaweiDictionarySyncBloodPressure());
ret.add(new HuaweiDictionarySyncLakeLouiseAMS());
return ret;
}
for(Integer cl: list) {
if((cl == HuaweiDictTypes.BODY_TEMPERATURE_CLASS || cl == HuaweiDictTypes.SKIN_TEMPERATURE_CLASS) && coordinator.supportsTemperature()) {
result.add(cl);
} else if(cl == HuaweiDictTypes.BLOOD_PRESSURE_CLASS && coordinator.supportsBloodPressure()) {
result.add(cl);
} else if(cl == HuaweiDictTypes.HRV_CLASS && coordinator.supportsHRV()) {
result.add(cl);
} else if(cl == HuaweiDictTypes.EMOTION_CLASS && coordinator.supportsEmotion()) {
result.add(cl);
} else if(cl == HuaweiDictTypes.SLEEP_APNEA_CLASS && coordinator.supportsSleepApnea()) {
result.add(cl);
public Map<Integer, HuaweiDictionarySyncInterface> getAllSupported(HuaweiCoordinator coordinator) {
List<HuaweiDictionarySyncInterface> handlers = getSyncHandlers();
Map<Integer, HuaweiDictionarySyncInterface> ret = new HashMap<>();
for(HuaweiDictionarySyncInterface i: handlers) {
if(i.supports(coordinator)) {
ret.put(i.getDataClass(), i);
}
}
return result;
return ret;
}
public void startSync(List<Integer> dictClasses, DictionarySyncCallback callback) {
LOG.info("P2PDataDictionarySyncService startSync {}", dictClasses);
if(callback == null) {
if (callback == null) {
LOG.error("P2PDataDictionarySyncService startSync callback is null");
return;
}
classesToSync = dictClasses;
if(classesToSync.isEmpty()) {
if (classesToSync.isEmpty()) {
callback.onComplete(false);
return;
}
@@ -115,7 +130,7 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
private void sendSyncRequest(int dictClass, long startTime, DictionarySyncCallback callback) {
LOG.info("P2PDataDictionarySyncService class {}", dictClass);
if(callback == null) {
if (callback == null) {
LOG.error("P2PDataDictionarySyncService sendSyncRequest callback is null");
return;
}
@@ -126,21 +141,21 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
return;
}
if(currentRequests.containsKey(dictClass)) {
if (currentRequests.containsKey(dictClass)) {
LOG.info("P2PDataDictionarySyncService current class in progress");
callback.onComplete(false);
return;
}
if(startTime == 0) {
if (startTime == 0) {
startTime = callback.onGetLastDataSyncTimestamp(dictClass);
}
if(startTime < 0) {
if (startTime < 0) {
LOG.info("P2PDataDictionarySyncService start time is less then 0");
callback.onComplete(false);
return;
}
if(startTime > 0) {
if (startTime > 0) {
startTime += 1000;
}
@@ -210,7 +225,7 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
@NonNull
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("HuaweiDictDataValue{");
final StringBuilder sb = new StringBuilder("HuaweiDictDataValue{");
sb.append("dataType=").append(dataType);
sb.append(", tag=").append(tag);
sb.append(", value=");
@@ -241,7 +256,9 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
this.data = data;
}
public int getDictClass() { return dictClass; }
public int getDictClass() {
return dictClass;
}
public long getStartTimestamp() {
return startTimestamp;
@@ -262,13 +279,11 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
@NonNull
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("HuaweiDictSample{");
sb.append("startTime=").append(startTimestamp);
sb.append(", endTime=").append(endTimestamp);
sb.append(", modifyTime=").append(modifyTimestamp);
sb.append(", data=").append(data);
sb.append('}');
return sb.toString();
return "HuaweiDictSample{" + "startTime=" + startTimestamp +
", endTime=" + endTimestamp +
", modifyTime=" + modifyTimestamp +
", data=" + data +
'}';
}
}
@@ -284,16 +299,16 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
int operation = tlv.getInteger(0x01);
int dictClass = tlv.getInteger(0x02);
if(!currentRequests.containsKey(dictClass)) {
if (!currentRequests.containsKey(dictClass)) {
return;
}
callback = currentRequests.remove(dictClass);
if(callback == null) {
if (callback == null) {
return;
}
if(operation != 1) {
if (operation != 1) {
return;
//I never see value differ from 1. So I don't know how to interpret others. Just ignore for now
//callback.onComplete(true);
@@ -301,7 +316,7 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
//NOTE: all tags with high bit set should be parsed as container
List<DictData> result = new ArrayList<>();
List<DictData> result = new ArrayList<>();
long lastTimestamp = 0;
@@ -335,7 +350,7 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
if (!result.isEmpty()) {
sendSyncRequest(dictClass, lastTimestamp, callback);
} else {
if(classesToSync.isEmpty()) {
if (classesToSync.isEmpty()) {
classesToSync = null;
callback.onComplete(true);
} else {
@@ -344,7 +359,7 @@ public class HuaweiP2PDataDictionarySyncService extends HuaweiBaseP2PService {
}
} catch (HuaweiPacket.MissingTagException e) {
LOG.error("P2PDataDictionarySyncService parse error", e);
if(callback != null) {
if (callback != null) {
callback.onComplete(false);
}
}
@@ -0,0 +1,39 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
//TODO: not fully discovered.
public class HuaweiDictionarySyncAltitude implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncAltitude.class);
public static final int ALTITUDE_CLASS = 200003;
@Override
public int getDataClass() {
return ALTITUDE_CLASS;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return false; // coordinator.supportsAltitude()
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
}
}
@@ -0,0 +1,39 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
// TODO: not fully discovered.
public class HuaweiDictionarySyncArterialStiffness implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncArterialStiffness.class);
public static final int ARTERIAL_STIFFNESS_CLASS = 400017;
@Override
public int getDataClass() {
return ARTERIAL_STIFFNESS_CLASS ;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return false; // coordinator.supportsArterialStiffnessDetection()
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
}
}
@@ -0,0 +1,39 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
// TODO: not fully discovered.
public class HuaweiDictionarySyncBloodPressure implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncBloodPressure.class);
public static final int BLOOD_PRESSURE_CLASS = 10002;
@Override
public int getDataClass() {
return BLOOD_PRESSURE_CLASS;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return false; //coordinator.supportsBloodPressure()
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
}
}
@@ -0,0 +1,108 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiEmotionsSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiUtil;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEmotionsSample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
public class HuaweiDictionarySyncEmotion implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncEmotion.class);
public static final int EMOTION_CLASS = 500031;
public static final int EMOTION_STATUS_VALUE = 500031195;
public static final int EMOTION_VALENCE_CHARACTER_VALUE = 500031347;
public static final int EMOTION_ORIGIN_STATUS_VALUE = 500031160;
public static final int EMOTION_AROUSAL_CHARACTER_VALUE = 500031842;
@Override
public int getDataClass() {
return EMOTION_CLASS;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return coordinator.supportsEmotion();
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
try (DBHandler db = GBApplication.acquireDB()) {
HuaweiEmotionsSampleProvider emotionsStatsSampleProvider = new HuaweiEmotionsSampleProvider(gbDevice, db.getDaoSession());
return emotionsStatsSampleProvider.getLastFetchTimestamp();
} catch (Exception e) {
LOG.warn("Exception for getting emotion start time", e);
}
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
List<HuaweiEmotionsSample> emotionsSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Integer status = null;
Double valenceCharacter = null;
Integer originStatus = null;
Double arousalCharacter = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() != 10) {
LOG.info("emotions unexpected tag: {}", val.getTag());
continue;
}
if (val.getDataType() == EMOTION_STATUS_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value < 100) {
status = (int) value;
}
} else if (val.getDataType() == EMOTION_VALENCE_CHARACTER_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value <= 100) {
valenceCharacter = value;
}
} else if (val.getDataType() == EMOTION_ORIGIN_STATUS_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value < 100) {
originStatus = (int) value;
}
} else if (val.getDataType() == EMOTION_AROUSAL_CHARACTER_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value <= 100) {
arousalCharacter = value;
}
} else {
LOG.info("emotions unknown data type: {}", val.getDataType());
}
}
if (status != null || valenceCharacter != null || originStatus != null || arousalCharacter != null) {
HuaweiEmotionsSample sample = new HuaweiEmotionsSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setStatus(status == null ? 0 : status);
sample.setValenceCharacter(valenceCharacter);
sample.setOriginStatus(originStatus);
sample.setArousalCharacter(arousalCharacter);
emotionsSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiEmotionsSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, emotionsSamples);
} catch (Exception e) {
LOG.error("Cannot save emotions samples, continue");
}
}
}
@@ -0,0 +1,84 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiHrvValueSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiUtil;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiHrvValueSample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
public class HuaweiDictionarySyncHRV implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncHRV.class);
public static final int HRV_CLASS = 500044;
public static final int HRV_RMSSD_VALUE = 500044831;
@Override
public int getDataClass() {
return HRV_CLASS;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return coordinator.supportsHRV();
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
try (DBHandler db = GBApplication.acquireDB()) {
HuaweiHrvValueSampleProvider hrvStatsSampleProvider = new HuaweiHrvValueSampleProvider(gbDevice, db.getDaoSession());
return hrvStatsSampleProvider.getLastFetchTimestamp();
} catch (Exception e) {
LOG.warn("Exception for getting HRV start time", e);
}
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
List<HuaweiHrvValueSample> hrvSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Integer rmssd = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() != 10) {
LOG.info("HRV unexpected tag: {}", val.getTag());
continue;
}
if (val.getDataType() == HRV_RMSSD_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 0 && value <= 200) {
rmssd = (int) value;
}
} else {
LOG.info("HRV unknown data type: {}", val.getDataType());
}
}
if(rmssd != null) {
HuaweiHrvValueSample sample = new HuaweiHrvValueSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setValue(rmssd);
hrvSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiHrvValueSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, hrvSamples);
} catch (Exception e) {
LOG.error("Cannot save HRV samples, continue");
}
}
}
@@ -0,0 +1,20 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
public interface HuaweiDictionarySyncInterface {
int getDataClass();
boolean supports(HuaweiCoordinator coordinator);
long getLastDataSyncTimestamp(GBDevice gbDevice);
void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData);
}
@@ -0,0 +1,40 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
// TODO: not fully discovered.
public class HuaweiDictionarySyncLakeLouiseAMS implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncLakeLouiseAMS.class);
public static final int LAKE_LOUISE_AMS_CLASS = 500006;
@Override
public int getDataClass() {
return LAKE_LOUISE_AMS_CLASS;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return false; // coordinator.supportsAltitude()
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
}
}
@@ -0,0 +1,85 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiDictTypes;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTemperatureSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiUtil;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiTemperatureSample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
public class HuaweiDictionarySyncSkinTemperature implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncSkinTemperature.class);
@Override
public int getDataClass() {
return HuaweiDictTypes.SKIN_TEMPERATURE_CLASS;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return coordinator.supportsTemperature();
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
try (DBHandler db = GBApplication.acquireDB()) {
HuaweiTemperatureSampleProvider sleepStatsSampleProvider = new HuaweiTemperatureSampleProvider(gbDevice, db.getDaoSession());
return sleepStatsSampleProvider.getLastFetchTimestamp();
} catch (Exception e) {
LOG.warn("Exception for getting skin temperature start time", e);
}
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
List<HuaweiTemperatureSample> temperatureSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Float temperature = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() != 10) {
LOG.info("skin temperature unexpected tag: {}", val.getTag());
continue;
}
if (val.getDataType() == HuaweiDictTypes.SKIN_TEMPERATURE_VALUE) {
double value = HuaweiUtil.convBytes2Double(val.getValue());
if (value >= 20 && value <= 42) {
temperature = (float) value;
}
} else {
LOG.info("skin temperature unknown data type: {}", val.getDataType());
}
}
if(temperature != null) {
HuaweiTemperatureSample sample = new HuaweiTemperatureSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setTemperature(temperature);
sample.setTemperatureType(TemperatureSample.TYPE_SKIN);
sample.setTemperatureLocation(TemperatureSample.LOCATION_WRIST);
temperatureSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiTemperatureSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, temperatureSamples);
} catch (Exception e) {
LOG.error("Cannot save skin temperature samples, continue");
}
}
}
@@ -0,0 +1,88 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.dictionarysync;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSleepApneaSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiUtil;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepApneaSample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PDataDictionarySyncService;
public class HuaweiDictionarySyncSleepApnea implements HuaweiDictionarySyncInterface {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDictionarySyncSleepApnea.class);
public static final int SLEEP_APNEA_CLASS = 500002;
public static final int SLEEP_APNEA_LEVEL_VALUE = 500002847;
@Override
public int getDataClass() {
return SLEEP_APNEA_CLASS;
}
@Override
public boolean supports(HuaweiCoordinator coordinator) {
return coordinator.supportsSleepApnea();
}
@Override
public long getLastDataSyncTimestamp(GBDevice gbDevice) {
try (DBHandler db = GBApplication.acquireDB()) {
HuaweiSleepApneaSampleProvider sleepApneaSampleProvider = new HuaweiSleepApneaSampleProvider(gbDevice, db.getDaoSession());
return sleepApneaSampleProvider.getLastFetchTimestamp();
} catch (Exception e) {
LOG.warn("Exception for getting sleep apnea start time", e);
}
return 0;
}
@Override
public void handleData(Context context, GBDevice gbDevice, List<HuaweiP2PDataDictionarySyncService.DictData> dictData) {
List<HuaweiSleepApneaSample> sleepApneaSamples = new ArrayList<>();
for (HuaweiP2PDataDictionarySyncService.DictData dt : dictData) {
long timestamp = dt.getStartTimestamp();
long lastTime = Math.max(dt.getEndTimestamp(), dt.getModifyTimestamp());
Integer level = null;
for (HuaweiP2PDataDictionarySyncService.DictData.DictDataValue val : dt.getData()) {
if (val.getTag() == 10) {
if (val.getDataType() == SLEEP_APNEA_LEVEL_VALUE) {
int value = (int) HuaweiUtil.convBytes2Double(val.getValue());
if (value == 1 || value == 2 || value == 3 || value == 4) {
level = value;
} else {
LOG.info("sleep apnea invalid value: {}", value);
}
} else {
LOG.info("sleep apnea unknown data type: {}", val.getDataType());
}
} else {
LOG.info("sleep apnea unsupported tag: {}", val.getTag());
}
}
if(level != null) {
LOG.info("APNEA timestamp: {} lastTime: {} level: {}", new Date(timestamp), lastTime, level);
HuaweiSleepApneaSample sample = new HuaweiSleepApneaSample();
sample.setTimestamp(timestamp);
sample.setLastTimestamp(lastTime);
sample.setLevel(level);
sleepApneaSamples.add(sample);
}
}
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession session = db.getDaoSession();
new HuaweiSleepApneaSampleProvider(gbDevice, session).persistForDevice(context, gbDevice, sleepApneaSamples);
} catch (Exception e) {
LOG.error("Cannot save sleep apnea samples, continue");
}
}
}