mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-30 14:02:56 +01:00
Sony WH-1000XM5: Add power off, fix battery, fix speak-to-chat fetch
This commit is contained in:
parent
7b3fbeb4af
commit
e0d481bb36
@ -48,7 +48,7 @@ public class SonyWH1000XM5Coordinator extends SonyHeadphonesCoordinator {
|
|||||||
// TODO R.xml.devicesettings_connect_two_devices,
|
// TODO R.xml.devicesettings_connect_two_devices,
|
||||||
// TODO automatic ANC depending on state (might need phone?)
|
// TODO automatic ANC depending on state (might need phone?)
|
||||||
SonyHeadphonesCapabilities.BatterySingle,
|
SonyHeadphonesCapabilities.BatterySingle,
|
||||||
// TODO SonyHeadphonesCapabilities.PowerOffFromPhone,
|
SonyHeadphonesCapabilities.PowerOffFromPhone,
|
||||||
SonyHeadphonesCapabilities.AmbientSoundControl,
|
SonyHeadphonesCapabilities.AmbientSoundControl,
|
||||||
SonyHeadphonesCapabilities.SpeakToChatEnabled,
|
SonyHeadphonesCapabilities.SpeakToChatEnabled,
|
||||||
SonyHeadphonesCapabilities.SpeakToChatConfig,
|
SonyHeadphonesCapabilities.SpeakToChatConfig,
|
||||||
|
@ -25,6 +25,7 @@ public enum PayloadTypeV2 {
|
|||||||
|
|
||||||
BATTERY_LEVEL_REQUEST(MessageType.COMMAND_1, 0x22),
|
BATTERY_LEVEL_REQUEST(MessageType.COMMAND_1, 0x22),
|
||||||
BATTERY_LEVEL_REPLY(MessageType.COMMAND_1, 0x23),
|
BATTERY_LEVEL_REPLY(MessageType.COMMAND_1, 0x23),
|
||||||
|
POWER_SET(MessageType.COMMAND_1, 0x24),
|
||||||
BATTERY_LEVEL_NOTIFY(MessageType.COMMAND_1, 0x25),
|
BATTERY_LEVEL_NOTIFY(MessageType.COMMAND_1, 0x25),
|
||||||
|
|
||||||
AUTOMATIC_POWER_OFF_GET(MessageType.COMMAND_1, 0x26),
|
AUTOMATIC_POWER_OFF_GET(MessageType.COMMAND_1, 0x26),
|
||||||
|
@ -365,8 +365,14 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Request powerOff() {
|
public Request powerOff() {
|
||||||
LOG.warn("Power off not implemented for V2");
|
return new Request(
|
||||||
return null;
|
PayloadTypeV2.POWER_SET.getMessageType(),
|
||||||
|
new byte[]{
|
||||||
|
PayloadTypeV2.POWER_SET.getCode(),
|
||||||
|
(byte) 0x03,
|
||||||
|
(byte) 0x01
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -661,6 +667,8 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
|||||||
@Override
|
@Override
|
||||||
protected BatteryType decodeBatteryType(final byte b) {
|
protected BatteryType decodeBatteryType(final byte b) {
|
||||||
switch (b) {
|
switch (b) {
|
||||||
|
case 0x00:
|
||||||
|
return BatteryType.SINGLE;
|
||||||
case 0x09:
|
case 0x09:
|
||||||
return BatteryType.DUAL;
|
return BatteryType.DUAL;
|
||||||
case 0x0a:
|
case 0x0a:
|
||||||
@ -673,10 +681,11 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
|
|||||||
@Override
|
@Override
|
||||||
protected byte encodeBatteryType(final BatteryType batteryType) {
|
protected byte encodeBatteryType(final BatteryType batteryType) {
|
||||||
switch (batteryType) {
|
switch (batteryType) {
|
||||||
|
case SINGLE:
|
||||||
|
return 0x00;
|
||||||
case DUAL:
|
case DUAL:
|
||||||
return 0x09;
|
return 0x09;
|
||||||
case CASE:
|
case CASE:
|
||||||
case SINGLE: // TODO: This is not the code for single, but we need to encode something
|
|
||||||
return 0x0a;
|
return 0x0a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,11 +19,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.pro
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType;
|
||||||
|
|
||||||
public enum PayloadTypeV3 {
|
public enum PayloadTypeV3 {
|
||||||
QUICK_ACCESS_GET(MessageType.COMMAND_1, 0xf6),
|
|
||||||
QUICK_ACCESS_RET(MessageType.COMMAND_1, 0xf7),
|
|
||||||
QUICK_ACCESS_SET(MessageType.COMMAND_1, 0xf8),
|
|
||||||
QUICK_ACCESS_NOTIFY(MessageType.COMMAND_1, 0xf9),
|
|
||||||
|
|
||||||
AMBIENT_SOUND_CONTROL_BUTTON_MODE_GET(MessageType.COMMAND_1, 0xfa),
|
AMBIENT_SOUND_CONTROL_BUTTON_MODE_GET(MessageType.COMMAND_1, 0xfa),
|
||||||
AMBIENT_SOUND_CONTROL_BUTTON_MODE_RET(MessageType.COMMAND_1, 0xfb),
|
AMBIENT_SOUND_CONTROL_BUTTON_MODE_RET(MessageType.COMMAND_1, 0xfb),
|
||||||
AMBIENT_SOUND_CONTROL_BUTTON_MODE_SET(MessageType.COMMAND_1, 0xfc),
|
AMBIENT_SOUND_CONTROL_BUTTON_MODE_SET(MessageType.COMMAND_1, 0xfc),
|
||||||
|
@ -135,9 +135,9 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 {
|
|||||||
@Override
|
@Override
|
||||||
public Request getQuickAccess() {
|
public Request getQuickAccess() {
|
||||||
return new Request(
|
return new Request(
|
||||||
PayloadTypeV3.QUICK_ACCESS_GET.getMessageType(),
|
PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(),
|
||||||
new byte[]{
|
new byte[]{
|
||||||
PayloadTypeV3.QUICK_ACCESS_GET.getCode(),
|
PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(),
|
||||||
(byte) 0x0d
|
(byte) 0x0d
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -146,9 +146,9 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 {
|
|||||||
@Override
|
@Override
|
||||||
public Request setQuickAccess(final QuickAccess quickAccess) {
|
public Request setQuickAccess(final QuickAccess quickAccess) {
|
||||||
return new Request(
|
return new Request(
|
||||||
PayloadTypeV3.QUICK_ACCESS_SET.getMessageType(),
|
PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(),
|
||||||
new byte[]{
|
new byte[]{
|
||||||
PayloadTypeV3.QUICK_ACCESS_SET.getCode(),
|
PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(),
|
||||||
(byte) 0x0d,
|
(byte) 0x0d,
|
||||||
(byte) 0x02,
|
(byte) 0x02,
|
||||||
quickAccess.getModeDoubleTap().getCode(),
|
quickAccess.getModeDoubleTap().getCode(),
|
||||||
@ -232,9 +232,6 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 {
|
|||||||
final PayloadTypeV3 payloadType = PayloadTypeV3.fromCode(messageType, payload[0]);
|
final PayloadTypeV3 payloadType = PayloadTypeV3.fromCode(messageType, payload[0]);
|
||||||
|
|
||||||
switch (payloadType) {
|
switch (payloadType) {
|
||||||
case QUICK_ACCESS_RET:
|
|
||||||
case QUICK_ACCESS_NOTIFY:
|
|
||||||
return handleQuickAccess(payload);
|
|
||||||
case AMBIENT_SOUND_CONTROL_BUTTON_MODE_RET:
|
case AMBIENT_SOUND_CONTROL_BUTTON_MODE_RET:
|
||||||
case AMBIENT_SOUND_CONTROL_BUTTON_MODE_NOTIFY:
|
case AMBIENT_SOUND_CONTROL_BUTTON_MODE_NOTIFY:
|
||||||
return handleAmbientSoundControlButtonMode(payload);
|
return handleAmbientSoundControlButtonMode(payload);
|
||||||
@ -350,6 +347,43 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 {
|
|||||||
return Collections.singletonList(event);
|
return Collections.singletonList(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends GBDeviceEvent> handleSpeakToChatEnabled(final byte[] payload) {
|
||||||
|
if (payload.length != 4) {
|
||||||
|
LOG.warn("Unexpected payload length {}", payload.length);
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload[1] != 0x0c) {
|
||||||
|
LOG.warn("Not speak to chat enabled, ignoring");
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Boolean disabled = booleanFromByte(payload[3]);
|
||||||
|
if (disabled == null) {
|
||||||
|
LOG.warn("Unknown speak to chat enabled code {}", String.format("%02x", payload[3]));
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("Speak to chat: {}", !disabled);
|
||||||
|
|
||||||
|
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
|
||||||
|
.withPreferences(new SpeakToChatEnabled(!disabled).toPreferences());
|
||||||
|
|
||||||
|
return Collections.singletonList(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<? extends GBDeviceEvent> handleAutomaticPowerOffButtonMode(final byte[] payload) {
|
||||||
|
switch (payload[1]) {
|
||||||
|
case 0x0c:
|
||||||
|
return handleSpeakToChatEnabled(payload);
|
||||||
|
case 0x0d:
|
||||||
|
return handleQuickAccess(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
public List<? extends GBDeviceEvent> handleVoiceNotifications(final byte[] payload) {
|
public List<? extends GBDeviceEvent> handleVoiceNotifications(final byte[] payload) {
|
||||||
if (payload.length != 4) {
|
if (payload.length != 4) {
|
||||||
LOG.warn("Unexpected payload length {}", payload.length);
|
LOG.warn("Unexpected payload length {}", payload.length);
|
||||||
|
@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakT
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications;
|
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.Request;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.Request;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.MockSonyCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.MockSonyCoordinator;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType;
|
||||||
|
|
||||||
public class SonyProtocolImplV3Test {
|
public class SonyProtocolImplV3Test {
|
||||||
private final MockSonyCoordinator coordinator = new MockSonyCoordinator();
|
private final MockSonyCoordinator coordinator = new MockSonyCoordinator();
|
||||||
@ -98,6 +99,20 @@ public class SonyProtocolImplV3Test {
|
|||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getBattery() {
|
||||||
|
final Map<BatteryType, String> commands = new LinkedHashMap<BatteryType, String>() {{
|
||||||
|
put(BatteryType.SINGLE, "22:00");
|
||||||
|
put(BatteryType.DUAL, "22:09");
|
||||||
|
put(BatteryType.CASE, "22:0a");
|
||||||
|
}};
|
||||||
|
|
||||||
|
for (Map.Entry<BatteryType, String> entry : commands.entrySet()) {
|
||||||
|
final Request request = protocol.getBattery(entry.getKey());
|
||||||
|
assertRequest(request, 0x0c, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getQuickAccess() {
|
public void getQuickAccess() {
|
||||||
final Request request = protocol.getQuickAccess();
|
final Request request = protocol.getQuickAccess();
|
||||||
@ -175,6 +190,12 @@ public class SonyProtocolImplV3Test {
|
|||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void powerOff() {
|
||||||
|
final Request request = protocol.powerOff();
|
||||||
|
assertRequest(request, 0x0c, "24:03:01");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void handleQuickAccess() {
|
public void handleQuickAccess() {
|
||||||
final Map<String, QuickAccess> commands = new LinkedHashMap<String, QuickAccess>() {{
|
final Map<String, QuickAccess> commands = new LinkedHashMap<String, QuickAccess>() {{
|
||||||
|
Loading…
Reference in New Issue
Block a user