diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM5Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM5Coordinator.java index 02879c796..ab42d5a12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM5Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM5Coordinator.java @@ -48,7 +48,7 @@ public class SonyWH1000XM5Coordinator extends SonyHeadphonesCoordinator { // TODO R.xml.devicesettings_connect_two_devices, // TODO automatic ANC depending on state (might need phone?) SonyHeadphonesCapabilities.BatterySingle, - // TODO SonyHeadphonesCapabilities.PowerOffFromPhone, + SonyHeadphonesCapabilities.PowerOffFromPhone, SonyHeadphonesCapabilities.AmbientSoundControl, SonyHeadphonesCapabilities.SpeakToChatEnabled, SonyHeadphonesCapabilities.SpeakToChatConfig, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/PayloadTypeV2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/PayloadTypeV2.java index 190433a5d..718d94abf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/PayloadTypeV2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/PayloadTypeV2.java @@ -25,6 +25,7 @@ public enum PayloadTypeV2 { BATTERY_LEVEL_REQUEST(MessageType.COMMAND_1, 0x22), BATTERY_LEVEL_REPLY(MessageType.COMMAND_1, 0x23), + POWER_SET(MessageType.COMMAND_1, 0x24), BATTERY_LEVEL_NOTIFY(MessageType.COMMAND_1, 0x25), AUTOMATIC_POWER_OFF_GET(MessageType.COMMAND_1, 0x26), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java index 52d71ea1c..53bdc24ec 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java @@ -365,8 +365,14 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 { @Override public Request powerOff() { - LOG.warn("Power off not implemented for V2"); - return null; + return new Request( + PayloadTypeV2.POWER_SET.getMessageType(), + new byte[]{ + PayloadTypeV2.POWER_SET.getCode(), + (byte) 0x03, + (byte) 0x01 + } + ); } @Override @@ -661,6 +667,8 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 { @Override protected BatteryType decodeBatteryType(final byte b) { switch (b) { + case 0x00: + return BatteryType.SINGLE; case 0x09: return BatteryType.DUAL; case 0x0a: @@ -673,10 +681,11 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 { @Override protected byte encodeBatteryType(final BatteryType batteryType) { switch (batteryType) { + case SINGLE: + return 0x00; case DUAL: return 0x09; case CASE: - case SINGLE: // TODO: This is not the code for single, but we need to encode something return 0x0a; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/PayloadTypeV3.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/PayloadTypeV3.java index 52cbd81d4..4d89bfd81 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/PayloadTypeV3.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/PayloadTypeV3.java @@ -19,11 +19,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.pro import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType; 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_RET(MessageType.COMMAND_1, 0xfb), AMBIENT_SOUND_CONTROL_BUTTON_MODE_SET(MessageType.COMMAND_1, 0xfc), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3.java index 8b542e791..59f043ccd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3.java @@ -135,9 +135,9 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { @Override public Request getQuickAccess() { return new Request( - PayloadTypeV3.QUICK_ACCESS_GET.getMessageType(), + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(), new byte[]{ - PayloadTypeV3.QUICK_ACCESS_GET.getCode(), + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(), (byte) 0x0d } ); @@ -146,9 +146,9 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { @Override public Request setQuickAccess(final QuickAccess quickAccess) { return new Request( - PayloadTypeV3.QUICK_ACCESS_SET.getMessageType(), + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(), new byte[]{ - PayloadTypeV3.QUICK_ACCESS_SET.getCode(), + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(), (byte) 0x0d, (byte) 0x02, quickAccess.getModeDoubleTap().getCode(), @@ -232,9 +232,6 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { final PayloadTypeV3 payloadType = PayloadTypeV3.fromCode(messageType, payload[0]); 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_NOTIFY: return handleAmbientSoundControlButtonMode(payload); @@ -350,6 +347,43 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { return Collections.singletonList(event); } + @Override + public List 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 handleAutomaticPowerOffButtonMode(final byte[] payload) { + switch (payload[1]) { + case 0x0c: + return handleSpeakToChatEnabled(payload); + case 0x0d: + return handleQuickAccess(payload); + } + + return Collections.emptyList(); + } + public List handleVoiceNotifications(final byte[] payload) { if (payload.length != 4) { LOG.warn("Unexpected payload length {}", payload.length); diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3Test.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3Test.java index 2fc5c74ae..6d56714a8 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3Test.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v3/SonyProtocolImplV3Test.java @@ -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.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.v1.params.BatteryType; public class SonyProtocolImplV3Test { private final MockSonyCoordinator coordinator = new MockSonyCoordinator(); @@ -98,6 +99,20 @@ public class SonyProtocolImplV3Test { }}); } + @Test + public void getBattery() { + final Map commands = new LinkedHashMap() {{ + put(BatteryType.SINGLE, "22:00"); + put(BatteryType.DUAL, "22:09"); + put(BatteryType.CASE, "22:0a"); + }}; + + for (Map.Entry entry : commands.entrySet()) { + final Request request = protocol.getBattery(entry.getKey()); + assertRequest(request, 0x0c, entry.getValue()); + } + } + @Test public void 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 public void handleQuickAccess() { final Map commands = new LinkedHashMap() {{