1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-01 06:22:55 +01:00

Use SoundcorePacket for Liberty 3 Pro

This commit is contained in:
Severin von Wnuck-Lipinski 2024-08-08 17:52:16 +02:00 committed by José Rebelo
parent c06e22b85e
commit 69441a6a79

View File

@ -8,10 +8,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -21,6 +19,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInf
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.SoundcorePacket;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -59,36 +58,34 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
} }
@Override @Override
public GBDeviceEvent[] decodeResponse(byte[] responseData) { public GBDeviceEvent[] decodeResponse(byte[] responseData) {
// Byte 0-4: Header ByteBuffer buf = ByteBuffer.wrap(responseData);
// Byte 5-6: Command (Audio-Mode) SoundcorePacket packet = SoundcorePacket.decode(buf);
// Byte 7: Size of data
// Byte 8-(x-1): Data if (packet == null)
// Byte x: Checksum return null;
if (responseData.length == 0) return null;
List<GBDeviceEvent> devEvts = new ArrayList<>(); List<GBDeviceEvent> devEvts = new ArrayList<>();
short cmd = packet.getCommand();
byte[] payload = packet.getPayload();
byte[] command = Arrays.copyOfRange(responseData, 5, 7); if (cmd == (short) 0x0101) {
byte[] data = Arrays.copyOfRange(responseData, 8, responseData.length-1);
if (Arrays.equals(command, new byte[]{0x01, 0x01})) {
// a lot of other data is in here, anything interesting? // a lot of other data is in here, anything interesting?
String firmware1 = readString(data, 7, 5); String firmware1 = readString(payload, 6, 5);
String firmware2 = readString(data, 12, 5); String firmware2 = readString(payload, 11, 5);
String serialNumber = readString(data, 17, 16); String serialNumber = readString(payload, 16, 16);
devEvts.add(buildVersionInfo(firmware1, firmware2, serialNumber)); devEvts.add(buildVersionInfo(firmware1, firmware2, serialNumber));
} else if (Arrays.equals(command, new byte[]{0x01, (byte) 0x8d})) { } else if (cmd == (short) 0x8d01) {
LOG.debug("Unknown incoming message - command: " + hexdump(command) + ", dump: " + hexdump(responseData)); LOG.debug("Unknown incoming message - command: " + cmd + ", dump: " + hexdump(responseData));
} else if (Arrays.equals(command, new byte[]{0x05, (byte) 0x82})) { } else if (cmd == (short) 0x8205) {
LOG.debug("Unknown incoming message - command: " + hexdump(command) + ", dump: " + hexdump(responseData)); LOG.debug("Unknown incoming message - command: " + cmd + ", dump: " + hexdump(responseData));
} else if (Arrays.equals(command, new byte[]{0x05, 0x01})) { } else if (cmd == (short) 0x0105) {
LOG.debug("Unknown incoming message - command: " + hexdump(command) + ", dump: " + hexdump(responseData)); LOG.debug("Unknown incoming message - command: " + cmd + ", dump: " + hexdump(responseData));
} else if (Arrays.equals(command, new byte[]{0x06, 0x01})) { //Sound Mode Update } else if (cmd == (short) 0x0106) { //Sound Mode Update
decodeAudioMode(data); decodeAudioMode(payload);
} else if (Arrays.equals(command, new byte[]{0x01, 0x03})) { // Battery Update } else if (cmd == (short) 0x0301) { // Battery Update
int batteryLeft = data[1] * 20; int batteryLeft = payload[0] * 20;
int batteryRight = data[2] * 20; int batteryRight = payload[1] * 20;
int batteryCase = data[3] * 20; int batteryCase = payload[2] * 20;
devEvts.add(buildBatteryInfo(battery_case, batteryCase)); devEvts.add(buildBatteryInfo(battery_case, batteryCase));
devEvts.add(buildBatteryInfo(battery_earphone_left, batteryLeft)); devEvts.add(buildBatteryInfo(battery_earphone_left, batteryLeft));
@ -96,7 +93,7 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
} else { } else {
// see https://github.com/gmallios/SoundcoreManager/blob/master/soundcore-lib/src/models/packet_kind.rs // see https://github.com/gmallios/SoundcoreManager/blob/master/soundcore-lib/src/models/packet_kind.rs
// for a mapping for other soundcore devices (similar protocol?) // for a mapping for other soundcore devices (similar protocol?)
LOG.debug("Unknown incoming message - command: " + hexdump(command) + ", dump: " + hexdump(responseData)); LOG.debug("Unknown incoming message - command: " + cmd + ", dump: " + hexdump(responseData));
} }
return devEvts.toArray(new GBDeviceEvent[devEvts.size()]); return devEvts.toArray(new GBDeviceEvent[devEvts.size()]);
} }
@ -107,25 +104,25 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
String soundmode = "off"; String soundmode = "off";
int anc_strength = 0; int anc_strength = 0;
if (payload[1] == 0x00) { if (payload[0] == 0x00) {
soundmode = "noise_cancelling"; soundmode = "noise_cancelling";
} else if (payload[1] == 0x01) { } else if (payload[0] == 0x01) {
soundmode = "ambient_sound"; soundmode = "ambient_sound";
} else if (payload[1] == 0x02) { } else if (payload[0] == 0x02) {
soundmode = "off"; soundmode = "off";
} }
if (payload[2] == 0x10) { if (payload[1] == 0x10) {
anc_strength = 0; anc_strength = 0;
} else if (payload[2] == 0x20) { } else if (payload[1] == 0x20) {
anc_strength = 1; anc_strength = 1;
} else if (payload[2] == 0x30) { } else if (payload[1] == 0x30) {
anc_strength = 2; anc_strength = 2;
} }
boolean vocal_mode = (payload[3] == 0x01); boolean vocal_mode = (payload[2] == 0x01);
boolean adaptive_anc = (payload[4] == 0x01); boolean adaptive_anc = (payload[3] == 0x01);
boolean windnoiseReduction = (payload[5] == 0x01); boolean windnoiseReduction = (payload[4] == 0x01);
editor.putString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, soundmode); editor.putString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, soundmode);
editor.putInt(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, anc_strength); editor.putInt(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, anc_strength);
@ -200,13 +197,13 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
// Miscellaneous Settings // Miscellaneous Settings
case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_DETECTION: case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_DETECTION:
boolean wearingDetection = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_DETECTION, false); boolean wearingDetection = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_DETECTION, false);
return encodeMessage((byte) 0x01, (byte) 0x81, new byte[]{0x00, encodeBoolean(wearingDetection)}); return new SoundcorePacket((short) 0x8101, new byte[]{encodeBoolean(wearingDetection)}).encode();
case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_TONE: case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_TONE:
boolean wearingTone = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_TONE, false); boolean wearingTone = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_TONE, false);
return encodeMessage((byte) 0x01, (byte) 0x8c, new byte[]{0x00, encodeBoolean(wearingTone)}); return new SoundcorePacket((short) 0x8c01, new byte[]{encodeBoolean(wearingTone)}).encode();
case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE: case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE:
boolean touchTone = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE, false); boolean touchTone = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE, false);
return encodeMessage((byte) 0x01, (byte) 0x83, new byte[]{0x00, encodeBoolean(touchTone)}); return new SoundcorePacket((short) 0x8301, new byte[]{encodeBoolean(touchTone)}).encode();
default: default:
LOG.debug("Unsupported CONFIG: " + config); LOG.debug("Unsupported CONFIG: " + config);
} }
@ -215,21 +212,19 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
} }
byte[] encodeDeviceInfoRequest() { byte[] encodeDeviceInfoRequest() {
byte[] payload = new byte[]{0x00}; return new SoundcorePacket((short) 0x0101).encode();
return encodeMessage((byte) 0x01, (byte) 0x01, payload);
} }
byte[] encodeMysteryDataRequest1() { byte[] encodeMysteryDataRequest1() {
byte[] payload = new byte[]{0x00, 0x00}; byte[] payload = new byte[]{0x00};
return encodeMessage((byte) 0x01, (byte) 0x8d, payload); return new SoundcorePacket((short) 0x8d01, payload).encode();
} }
byte[] encodeMysteryDataRequest2() { byte[] encodeMysteryDataRequest2() {
byte[] payload = new byte[]{0x00}; return new SoundcorePacket((short) 0x0105).encode();
return encodeMessage((byte) 0x05, (byte) 0x01, payload);
} }
byte[] encodeMysteryDataRequest3() { byte[] encodeMysteryDataRequest3() {
byte[] payload = new byte[]{0x00, 0x00}; byte[] payload = new byte[]{0x00};
return encodeMessage((byte) 0x05, (byte) 0x82, payload); return new SoundcorePacket((short) 0x8205, payload).encode();
} }
/** /**
@ -280,8 +275,8 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
byte vocal_mode = encodeBoolean(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, false)); byte vocal_mode = encodeBoolean(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, false));
byte windnoise_reduction = encodeBoolean(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, false)); byte windnoise_reduction = encodeBoolean(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, false));
byte[] payload = new byte[]{0x00, anc_mode, anc_strength, vocal_mode, adaptive_anc, windnoise_reduction, 0x01}; byte[] payload = new byte[]{anc_mode, anc_strength, vocal_mode, adaptive_anc, windnoise_reduction, 0x01};
return encodeMessage((byte) 0x06, (byte) 0x81, payload); return new SoundcorePacket((short) 0x8106, payload).encode();
} }
/** /**
@ -307,8 +302,8 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
LOG.error("Invalid Tap action"); LOG.error("Invalid Tap action");
return null; return null;
} }
payload = new byte[]{0x00, 0x00, action.getCode(), enabled_byte}; payload = new byte[]{0x00, action.getCode(), enabled_byte};
return encodeMessage((byte) 0x04, (byte) 0x83, payload); return new SoundcorePacket((short) 0x8304, payload).encode();
} }
/** /**
@ -335,8 +330,8 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
LOG.error("Invalid Tap action"); LOG.error("Invalid Tap action");
return null; return null;
} }
byte[] payload = new byte[] {0x00, encodeBoolean(right), action.getCode(), function_byte}; byte[] payload = new byte[] {encodeBoolean(right), action.getCode(), function_byte};
return encodeMessage((byte) 0x04, (byte) 0x81, payload); return new SoundcorePacket((short) 0x8104, payload).encode();
} }
/** /**
@ -346,32 +341,11 @@ public class SoundcoreLibertyProtocol extends GBDeviceProtocol {
private byte[] encodeControlAmbientModeMessage(boolean anc, boolean transparency, boolean normal) { private byte[] encodeControlAmbientModeMessage(boolean anc, boolean transparency, boolean normal) {
// Original app does not allow only one true flag. Unsure if Earbuds accept this state. // Original app does not allow only one true flag. Unsure if Earbuds accept this state.
byte ambientModes = (byte) (4 * (normal?1:0) + 2 * (transparency?1:0) + (anc?1:0)); byte ambientModes = (byte) (4 * (normal?1:0) + 2 * (transparency?1:0) + (anc?1:0));
return encodeMessage((byte) 0x06, (byte) 0x82, new byte[] {0x00, ambientModes}); return new SoundcorePacket((short) 0x8206, new byte[] {ambientModes}).encode();
} }
private byte encodeBoolean(boolean bool) { private byte encodeBoolean(boolean bool) {
if (bool) return 0x01; if (bool) return 0x01;
else return 0x00; else return 0x00;
} }
private byte[] encodeMessage(byte command1, byte command2, byte[] payload) {
int size = 8 + payload.length + 1;
ByteBuffer msgBuf = ByteBuffer.allocate(size);
msgBuf.order(ByteOrder.BIG_ENDIAN);
msgBuf.put(new byte[] {0x08, (byte) 0xee, 0x00, 0x00, 0x00}); // header
msgBuf.put(command1);
msgBuf.put(command2);
msgBuf.put((byte) size);
msgBuf.put(payload);
byte checksum = -10;
checksum += command1 + command2 + size;
for (int b : payload) {
checksum += b;
}
msgBuf.put(checksum);
return msgBuf.array();
}
} }