Huawei: Pulse wave arrhythmia activation and control. No sync to GB.

This commit is contained in:
Me7c7
2025-10-12 14:26:57 +03:00
parent 7ee1f67c54
commit eae5d87ae0
10 changed files with 243 additions and 3 deletions
@@ -114,7 +114,9 @@ public final class HuaweiConstants {
public static final String PREF_HUAWEI_ACTIVITY_REMINDER_STAND = "pref_huawei_activity_reminder_stand";
public static final String PREF_HUAWEI_ACTIVITY_REMINDER_PROGRESS = "pref_huawei_activity_reminder_progress";
public static final String PREF_HUAWEI_ACTIVITY_REMINDER_GOAL_REACHED = "pref_huawei_activity_reminder_goal_reached";
public static final String PREF_HUAWEI_ARRHYTHMIA_SWITCH = "pref_huawei_arrhythmia_switch";
public static final String PREF_HUAWEI_ARRHYTHMIA_AUTOMATIC = "pref_huawei_arrhythmia_automatic";
public static final String PREF_HUAWEI_ARRHYTHMIA_ALERT = "pref_huawei_arrhythmia_alert";
public static final String PKG_NAME = "com.huawei.devicegroupmanage";
@@ -321,6 +321,11 @@ public class HuaweiCoordinator {
if(supportsAutoStress()) {
deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH, R.xml.devicesettings_huawei_stress);
}
if(supportsArrhythmia() && isShowForceCountrySpecificFeatures(device)) {
deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH, R.xml.devicesettings_huawei_arrhythmia);
}
if(supportsThreeCircle() || supportsThreeCircleLite()) {
deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH, R.xml.devicesettings_huawei_activity_reminders);
}
@@ -370,7 +375,7 @@ public class HuaweiCoordinator {
}
if(supportsSendCountryCode()) {
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_huawei_country_code);
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_huawei_features);
}
// Time
@@ -912,6 +917,12 @@ public class HuaweiCoordinator {
return false;
}
public boolean supportsArrhythmia() {
if (supportsExpandCapability())
return supportsExpandCapability(168); // 113
return false;
}
public boolean supportsPromptPushMessage () {
// do not ask for capabilities under specific condition
// if (deviceType == 10 && deviceVersion == 73617766697368 && deviceSoftVersion == 372E312E31) -> leo device
@@ -1173,5 +1184,8 @@ public class HuaweiCoordinator {
return getDeviceSpecificSharedPreferences(gbDevice).getBoolean("pref_huawei_country_code_enable", false) && !TextUtils.isEmpty(getCountryCode(gbDevice));
}
public boolean isShowForceCountrySpecificFeatures(GBDevice gbDevice) {
return getDeviceSpecificSharedPreferences(gbDevice).getBoolean("pref_huawei_force_features_settings_switch", false) || getSendCountryCodeEnabled(gbDevice);
}
}
@@ -25,4 +25,6 @@ public class HuaweiDictTypes {
public static final int SKIN_TEMPERATURE_VALUE = 400012430;
public static final int SLEEP_DETAILS_CLASS = 700013;
public static final int ARRHYTHMIA_CLASS = 700004;
}
@@ -45,6 +45,9 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_ACTIVITY_REMINDER_GOAL_REACHED;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_ACTIVITY_REMINDER_PROGRESS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_ACTIVITY_REMINDER_STAND;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_ALERT;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_AUTOMATIC;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_SWITCH;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_DEBUG_REQUEST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_CONTINUOUS_SKIN_TEMPERATURE_MEASUREMENT;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.PREF_HUAWEI_HEART_RATE_HIGH_ALERT;
@@ -137,6 +140,10 @@ public class HuaweiSettingsCustomizer implements DeviceSpecificSettingsCustomize
handler.addPreferenceHandlerFor(PREF_HUAWEI_ACTIVITY_REMINDER_PROGRESS);
handler.addPreferenceHandlerFor(PREF_HUAWEI_ACTIVITY_REMINDER_GOAL_REACHED);
handler.addPreferenceHandlerFor(PREF_HUAWEI_ARRHYTHMIA_SWITCH);
handler.addPreferenceHandlerFor(PREF_HUAWEI_ARRHYTHMIA_AUTOMATIC);
handler.addPreferenceHandlerFor(PREF_HUAWEI_ARRHYTHMIA_ALERT);
final Preference forceOptions = handler.findPreference(PREF_FORCE_OPTIONS);
if (forceOptions != null) {
@@ -135,6 +135,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.datasync.HuaweiDataSyncArrhythmia;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.datasync.HuaweiDataSyncFeatureManager;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.datasync.HuaweiDataSyncEmotion;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.datasync.HuaweiDataSyncFindDevice;
@@ -305,6 +306,8 @@ public class HuaweiSupportProvider {
private HuaweiDataSyncEmotion huaweiDataSyncEmotion = null;
private HuaweiDataSyncArrhythmia huaweiDataSyncArrhythmia = null;
protected HuaweiOTAManager huaweiOTAManager = new HuaweiOTAManager(this);
HuaweiStressCalibration stressCalibration = null;
@@ -866,6 +869,10 @@ public class HuaweiSupportProvider {
huaweiDataSyncEmotion = new HuaweiDataSyncEmotion(HuaweiSupportProvider.this);
}
if (getHuaweiCoordinator().supportsArrhythmia() && getHuaweiCoordinator().isShowForceCountrySpecificFeatures(getCoordinator().getDevice())) {
huaweiDataSyncArrhythmia = new HuaweiDataSyncArrhythmia(HuaweiSupportProvider.this);
}
// All of the below check that they are supported and otherwise they skip themselves
final List<Request> initRequestQueue = new ArrayList<>();
initRequestQueue.add(new SendExtendedAccountRequest(this));
@@ -1199,6 +1206,15 @@ public class HuaweiSupportProvider {
case HuaweiConstants.PREF_HUAWEI_STRESS_CALIBRATE:
calibrateStress();
break;
case HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_SWITCH:
activateArrhythmia();
break;
case HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_AUTOMATIC:
setArrhythmiaAutomatic();
break;
case HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_ALERT:
setArrhythmiaAlert();
break;
case DeviceSettingsPreferenceConst.PREF_FORCE_ENABLE_SMART_ALARM:
getAlarms();
break;
@@ -2489,6 +2505,40 @@ public class HuaweiSupportProvider {
return HuaweiStressParser.stressDataFromJsonStr(str);
}
private void activateArrhythmia() {
if(huaweiDataSyncArrhythmia != null) {
boolean arrhythmiaEnabled = GBApplication
.getDeviceSpecificSharedPrefs(getDevice().getAddress())
.getBoolean(HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_SWITCH, false);
if(!huaweiDataSyncArrhythmia.changeState(arrhythmiaEnabled)) {
LOG.error("Error Arrhythmia change state");
}
}
}
private void setArrhythmiaAutomatic() {
if(huaweiDataSyncArrhythmia != null) {
boolean automaticArrhythmiaEnabled = GBApplication
.getDeviceSpecificSharedPrefs(getDevice().getAddress())
.getBoolean(HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_AUTOMATIC, false);
if(!huaweiDataSyncArrhythmia.setAutomatic(automaticArrhythmiaEnabled)) {
LOG.error("Error Arrhythmia change automatic");
}
}
}
private void setArrhythmiaAlert() {
if(huaweiDataSyncArrhythmia != null) {
boolean arrhythmiaAlertEnabled = GBApplication
.getDeviceSpecificSharedPrefs(getDevice().getAddress())
.getBoolean(HuaweiConstants.PREF_HUAWEI_ARRHYTHMIA_ALERT, false);
if(!huaweiDataSyncArrhythmia.setAlert(arrhythmiaAlertEnabled)) {
LOG.error("Error Arrhythmia change alert");
}
}
}
public void sendDebugRequest() {
try {
LOG.debug("Send debug request");
@@ -0,0 +1,99 @@
/* Copyright (C) 2025 Me7c7
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.datasync;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class HuaweiDataSyncArrhythmia implements HuaweiDataSyncCommon.DataCallback {
private final Logger LOG = LoggerFactory.getLogger(HuaweiDataSyncArrhythmia.class);
private final HuaweiSupportProvider support;
public static final String SRC_PKG_NAME = "hw.health.ppgjsmodule";
public static final String PKG_NAME = "hw.watch.health.arrhythmia";
public HuaweiDataSyncArrhythmia(HuaweiSupportProvider support) {
this.support = support;
this.support.getHuaweiDataSyncManager().registerCallback(PKG_NAME, this);
}
private boolean sendCommonData(int configId, byte configAction, byte[] configData) {
HuaweiDataSyncCommon.ConfigCommandData data = new HuaweiDataSyncCommon.ConfigCommandData();
HuaweiDataSyncCommon.ConfigData config = new HuaweiDataSyncCommon.ConfigData();
config.configId = configId;
config.configAction = configAction;
config.configData = configData;
List<HuaweiDataSyncCommon.ConfigData> list = new ArrayList<>();
list.add(config);
data.setConfigDataList(list);
return this.support.getHuaweiDataSyncManager().sendConfigCommand(SRC_PKG_NAME, PKG_NAME, data);
}
public boolean changeState(boolean state) {
HuaweiTLV tlv = new HuaweiTLV().put(0x01, state);
return sendCommonData(900300004, (byte) 1, tlv.serialize());
}
// TODO:
public boolean queryState(boolean state) {
HuaweiTLV tlv = new HuaweiTLV().put(0x01, state);
return sendCommonData(900300004, (byte) 2, tlv.serialize());
}
// TODO:
public boolean measure(byte command) { // 1 - start, 0 - end
HuaweiTLV tlv = new HuaweiTLV().put(0x01, command);
return sendCommonData(900300003, (byte) 1, tlv.serialize());
}
public boolean setAutomatic(boolean state) {
HuaweiTLV tlv = new HuaweiTLV().put(0x01, state);
return sendCommonData(900300002, (byte) 1, tlv.serialize());
}
public boolean setAlert(boolean state) {
HuaweiTLV tlv = new HuaweiTLV().put(0x01, state);
return sendCommonData(900300009, (byte) 1, tlv.serialize());
}
@Override
public void onConfigCommand(HuaweiDataSyncCommon.ConfigCommandData data) {
LOG.info("Handle Arrhythmia command");
//TODO:
}
@Override
public void onEventCommand(HuaweiDataSyncCommon.EventCommandData data) {
}
@Override
public void onDataCommand(HuaweiDataSyncCommon.DataCommandData data) {
}
@Override
public void onDictDataCommand(HuaweiDataSyncCommon.DictDataCommandData data) {
}
}
@@ -1,3 +1,19 @@
/* Copyright (C) 2025 Me7c7
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.datasync;
import com.google.gson.Gson;
+10 -1
View File
@@ -4377,8 +4377,17 @@
<string name="pref_huawei_features_config_info_title">Enable additional health features</string>
<string name="pref_huawei_features_config_info_summary">Enabling the settings below will activate additional health features on your device. For example, emotional or ECG.\nNOTE: The available features depend on the device.</string>
<string name="pref_huawei_features_config_warning_title">Send country code and features config to the device.</string>
<string name="pref_huawei_features_config_warning_summary">Warning: Enabling these settings when both Gadgetbridge and the vendor app are in use can cause issues with the vendor app and device functionality or can not affect current device abilities at all. Enable at your own risk.\nDevice reconnect is required.</string>
<string name="pref_huawei_features_config_warning_summary">Warning: Enabling these settings when both Gadgetbridge and the vendor app are in use can cause issues with the vendor app and device functionality or can not affect current device abilities at all. Setting incorrect country can affect other device settings, such as the region-based frequency configuration etc. Enable at your own risk.\nDevice reconnect is required.</string>
<string name="pref_huawei_send_country_code_title">Send country code</string>
<string name="pref_huawei_send_country_code_summary">Send country code to the device.\n Attention: country code should be set</string>
<string name="pref_huawei_country_code_title">Country code</string>
<string name="pref_huawei_force_features_settings_title">Force to show country-restricted settings</string>
<string name="pref_huawei_force_features_settings_summary">Force to show the country specific settings in the Gadgetbridge app.\n Warning: Gadgetbridge will display the settings reported by your device as supported. Please note that some settings may be restricted by the vendor app on your device and may not work, or may work incorrect.</string>
<string name="pref_huawei_pulse_wave_arrhythmia">Pulse wave arrhythmia</string>
<string name="pref_huawei_pulse_wave_arrhythmia_enable_title">Enable pulse wave arrhythmia</string>
<string name="pref_huawei_pulse_wave_arrhythmia_enable_summary">Activate Pulse wave arrhythmia on the device</string>
<string name="pref_huawei_pulse_wave_arrhythmia_automatic_title">Automatic arrhythmia detection</string>
<string name="pref_huawei_pulse_wave_arrhythmia_automatic_summary">The device will automatically monitor your heart rate.</string>
<string name="pref_huawei_pulse_wave_arrhythmia_alert_title">Arrhythmia alert</string>
<string name="pref_huawei_pulse_wave_arrhythmia_alert_summary">Receive alert when your heart rate is abnormal</string>
</resources>
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_heartrate"
android:key="screen_huawei_arrhythmia"
android:persistent="false"
android:title="@string/pref_huawei_pulse_wave_arrhythmia">
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_heartrate"
android:key="pref_huawei_arrhythmia_switch"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_huawei_pulse_wave_arrhythmia_enable_title"
android:summary="@string/pref_huawei_pulse_wave_arrhythmia_enable_summary" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_heartrate"
android:key="pref_huawei_arrhythmia_automatic"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_huawei_pulse_wave_arrhythmia_automatic_title"
android:summary="@string/pref_huawei_pulse_wave_arrhythmia_automatic_summary" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_heartrate"
android:key="pref_huawei_arrhythmia_alert"
android:layout="@layout/preference_checkbox"
android:title="@string/pref_huawei_pulse_wave_arrhythmia_alert_title"
android:summary="@string/pref_huawei_pulse_wave_arrhythmia_alert_summary" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>
@@ -35,5 +35,11 @@
android:key="pref_huawei_country_code"
android:title="@string/pref_huawei_country_code_title"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="pref_huawei_force_features_settings_switch"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_huawei_force_features_settings_summary"
android:title="@string/pref_huawei_force_features_settings_title" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>