diff --git a/CHANGELOG.md b/CHANGELOG.md index 30e916659..f5f555c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Next Version * Initial support for Amazfit GTR 3 Pro +* Initial support for Sony WH-1000XM5 * Amazfit Bip U: Remove alarm snooze option * Amazfit GTR 4 / GTS 4: Add watch Wi-Fi Hotspot and FTP Server * Amazfit GTR 4 / GTS 4: Perform and receive phone calls on watch diff --git a/README.md b/README.md index 1b7733801..967d85fc6 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ vendor's servers. - [SMA](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/SMA) Q2 (SMA-Q2-OSS Firmware) - [Sony Headphones](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Sony-Headphones) - LinkBuds S - - WH-1000XM2, WH-1000XM3, WH-1000XM4 + - WH-1000XM2, WH-1000XM3, WH-1000XM4, WH-1000XM5 - WF-SP800N - WF-1000XM3, WF-1000XM4 - Teclast H10, H30 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 new file mode 100644 index 000000000..02879c796 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM5Coordinator.java @@ -0,0 +1,65 @@ +/* Copyright (C) 2023 José Rebelo + + 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 . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators; + +import androidx.annotation.NonNull; + +import java.util.Arrays; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCapabilities; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class SonyWH1000XM5Coordinator extends SonyHeadphonesCoordinator { + @NonNull + @Override + public DeviceType getSupportedType(final GBDeviceCandidate candidate) { + if (candidate.getName().contains("WH-1000XM5")) { + return DeviceType.SONY_WH_1000XM5; + } + + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.SONY_WH_1000XM5; + } + + @Override + public List getCapabilities() { + return Arrays.asList( + // TODO R.xml.devicesettings_connect_two_devices, + // TODO automatic ANC depending on state (might need phone?) + SonyHeadphonesCapabilities.BatterySingle, + // TODO SonyHeadphonesCapabilities.PowerOffFromPhone, + SonyHeadphonesCapabilities.AmbientSoundControl, + SonyHeadphonesCapabilities.SpeakToChatEnabled, + SonyHeadphonesCapabilities.SpeakToChatConfig, + // TODO SonyHeadphonesCapabilities.AudioUpsampling, + // TODO SonyHeadphonesCapabilities.AmbientSoundControlButtonMode, + SonyHeadphonesCapabilities.VoiceNotifications, + SonyHeadphonesCapabilities.AutomaticPowerOffWhenTakenOff, + // TODO SonyHeadphonesCapabilities.TouchSensorSingle, + SonyHeadphonesCapabilities.EqualizerWithCustomBands, + SonyHeadphonesCapabilities.QuickAccess, + SonyHeadphonesCapabilities.PauseWhenTakenOff + ); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index e8121df88..a7a32614c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -124,6 +124,7 @@ public enum DeviceType { SONY_WH_1000XM2(434, R.drawable.ic_device_sony_overhead, R.drawable.ic_device_sony_overhead_disabled, R.string.devicetype_sony_wh_1000xm2), SONY_WF_1000XM4(435, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_wf_1000xm4), SONY_LINKBUDS_S(436, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_linkbuds_s), + SONY_WH_1000XM5(437, R.drawable.ic_device_sony_overhead, R.drawable.ic_device_sony_overhead_disabled, R.string.devicetype_sony_wh_1000xm5), BOSE_QC35(440, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_bose_qc35), VESC_NRF(500, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc), VESC_HM10(501, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 7a27663fe..d5ba1829b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -353,6 +353,8 @@ public class DeviceSupportFactory { return new ServiceDeviceSupport(new SonyHeadphonesSupport()); case SONY_LINKBUDS_S: return new ServiceDeviceSupport(new SonyHeadphonesSupport()); + case SONY_WH_1000XM5: + return new ServiceDeviceSupport(new SonyHeadphonesSupport()); case VESC_NRF: case VESC_HM10: return new ServiceDeviceSupport(new VescDeviceSupport(device.getType())); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java index 56be60083..88d6eff2a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java @@ -117,8 +117,11 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { break; case 0x03: // LinkBuds S 2.0.2: 01:00:03:00:00:07:00:00 - default: + // WH-1000XM5 1.1.3: 01:00:03:00:00:00:00:00 protocolImpl = new SonyProtocolImplV3(getDevice()); + break; + default: + LOG.error("Unexpected version for payload of length 8: {}", message.getPayload()[2]); return null; } } else { 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 f006ba27d..8b542e791 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 @@ -21,24 +21,22 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences; -import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCapabilities; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes; -import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChatConfig; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChatEnabled; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.MessageType; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.Request; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.PayloadTypeV1; -import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType; -import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v2.PayloadTypeV2; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v2.SonyProtocolImplV2; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -62,7 +60,7 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { @Override public Request setAmbientSoundControl(final AmbientSoundControl ambientSoundControl) { - final ByteBuffer buf = ByteBuffer.allocate(8); + final ByteBuffer buf = ByteBuffer.allocate(7); buf.put(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getCode()); buf.put((byte) 0x17); @@ -86,6 +84,54 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { return new Request(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array()); } + @Override + public Request setSpeakToChatEnabled(final SpeakToChatEnabled config) { + return new Request( + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(), + new byte[]{ + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(), + (byte) 0x0c, + (byte) (config.isEnabled() ? 0x00 : 0x01), // TODO it's reversed? + (byte) 0x01 + } + ); + } + + @Override + public Request getSpeakToChatEnabled() { + return new Request( + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(), + new byte[]{ + PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(), + (byte) 0x0c + } + ); + } + + @Override + public Request setSpeakToChatConfig(final SpeakToChatConfig config) { + return new Request( + PayloadTypeV1.SPEAK_TO_CHAT_CONFIG_SET.getMessageType(), + new byte[]{ + PayloadTypeV1.SPEAK_TO_CHAT_CONFIG_SET.getCode(), + (byte) 0x0c, + config.getSensitivity().getCode(), + config.getTimeout().getCode() + } + ); + } + + @Override + public Request getSpeakToChatConfig() { + return new Request( + PayloadTypeV1.SPEAK_TO_CHAT_CONFIG_GET.getMessageType(), + new byte[]{ + PayloadTypeV1.SPEAK_TO_CHAT_CONFIG_GET.getCode(), + (byte) 0x0c + } + ); + } + @Override public Request getQuickAccess() { return new Request( @@ -138,6 +184,49 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { ); } + @Override + public Request setEqualizerCustomBands(final EqualizerCustomBands config) { + final ByteBuffer buf = ByteBuffer.allocate(10); + + buf.put(PayloadTypeV1.EQUALIZER_SET.getCode()); + buf.put((byte) 0x00); + buf.put((byte) 0xa0); + buf.put((byte) 0x06); + + buf.put((byte) (config.getBass() + 10)); + for (final Integer band : config.getBands()) { + buf.put((byte) (band + 10)); + } + + return new Request( + PayloadTypeV1.EQUALIZER_SET.getMessageType(), + buf.array() + ); + } + + @Override + public Request getVoiceNotifications() { + return new Request( + PayloadTypeV1.VOICE_NOTIFICATIONS_GET.getMessageType(), + new byte[]{ + PayloadTypeV1.VOICE_NOTIFICATIONS_GET.getCode(), + (byte) 0x01 + } + ); + } + + @Override + public Request setVoiceNotifications(final VoiceNotifications config) { + return new Request( + PayloadTypeV1.VOICE_NOTIFICATIONS_SET.getMessageType(), + new byte[]{ + PayloadTypeV1.VOICE_NOTIFICATIONS_SET.getCode(), + (byte) 0x01, + (byte) (config.isEnabled() ? 0x00 : 0x01) // reversed? + } + ); + } + @Override public List handlePayload(final MessageType messageType, final byte[] payload) { final PayloadTypeV3 payloadType = PayloadTypeV3.fromCode(messageType, payload[0]); @@ -261,6 +350,35 @@ public class SonyProtocolImplV3 extends SonyProtocolImplV2 { return Collections.singletonList(event); } + public List handleVoiceNotifications(final byte[] payload) { + if (payload.length != 4) { + LOG.warn("Unexpected payload length {}", payload.length); + return Collections.emptyList(); + } + + boolean enabled; + + // reversed? + switch (payload[2]) { + case 0x00: + enabled = true; + break; + case 0x01: + enabled = false; + break; + default: + LOG.warn("Unknown voice notifications code {}", String.format("%02x", payload[3])); + return Collections.emptyList(); + } + + LOG.debug("Voice Notifications: {}", enabled); + + final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() + .withPreferences(new VoiceNotifications(enabled).toPreferences()); + + return Collections.singletonList(event); + } + @Override protected ButtonModes.Mode decodeButtonMode(final byte b) { switch (b) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 64618bb3c..02806f8ac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -50,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts4mini.Amazfi import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfittrex2.AmazfitTRex2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyLinkBudsSCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.soflow.SoFlowCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyWH1000XM5Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.supercars.SuperCarsCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.fitpro.FitProDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; @@ -363,6 +364,7 @@ public class DeviceHelper { result.add(new SonyWF1000XM3Coordinator()); result.add(new SonyWH1000XM2Coordinator()); result.add(new SonyWF1000XM4Coordinator()); + result.add(new SonyWH1000XM5Coordinator()); result.add(new QC35Coordinator()); result.add(new BinarySensorCoordinator()); result.add(new FlipperZeroCoordinator()); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8a94db093..ef8801043 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1276,6 +1276,7 @@ Sony WH-1000XM2 Sony WH-1000XM3 Sony WH-1000XM4 + Sony WH-1000XM5 Sony WF-SP800N Sony WF-1000XM3 Sony WF-1000XM4 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 4f3d7c71e..2fc5c74ae 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 @@ -20,10 +20,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.assertPrefs; import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.assertRequest; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.assertRequests; import static nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.SonyTestUtils.handleMessage; +import org.junit.Before; import org.junit.Test; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -32,19 +35,69 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyLinkBudsSCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChatConfig; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChatEnabled; +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; public class SonyProtocolImplV3Test { + private final MockSonyCoordinator coordinator = new MockSonyCoordinator(); private final SonyProtocolImplV3 protocol = new SonyProtocolImplV3(null) { @Override protected SonyHeadphonesCoordinator getCoordinator() { - return new SonyLinkBudsSCoordinator(); + return coordinator; } }; + @Before + public void before() { + coordinator.getCapabilities().clear(); + } + + @Test + public void setAmbientSoundControl() { + final Map commands = new LinkedHashMap() {{ + put(new AmbientSoundControl(AmbientSoundControl.Mode.AMBIENT_SOUND, false, 20), "68:17:01:01:01:00:14"); + put(new AmbientSoundControl(AmbientSoundControl.Mode.OFF, false, 20), "68:17:01:00:00:00:14"); + put(new AmbientSoundControl(AmbientSoundControl.Mode.AMBIENT_SOUND, false, 10), "68:17:01:01:01:00:0a"); + put(new AmbientSoundControl(AmbientSoundControl.Mode.AMBIENT_SOUND, true, 20), "68:17:01:01:01:01:14"); + put(new AmbientSoundControl(AmbientSoundControl.Mode.NOISE_CANCELLING, false, 20), "68:17:01:01:00:00:14"); + }}; + + for (Map.Entry entry : commands.entrySet()) { + final Request request = protocol.setAmbientSoundControl(entry.getKey()); + assertRequest(request, 0x0c, entry.getValue()); + } + } + + @Test + public void setSpeakToChatEnabled() { + assertRequests(protocol::setSpeakToChatEnabled, new LinkedHashMap() {{ + put(new SpeakToChatEnabled(false), "f8:0c:01:01"); + put(new SpeakToChatEnabled(true), "f8:0c:00:01"); + }}); + } + + @Test + public void setSpeakToChatConfig() { + assertRequests(protocol::setSpeakToChatConfig, new LinkedHashMap() {{ + put(new SpeakToChatConfig(false, SpeakToChatConfig.Sensitivity.HIGH, SpeakToChatConfig.Timeout.STANDARD), "fc:0c:01:01"); + put(new SpeakToChatConfig(false, SpeakToChatConfig.Sensitivity.LOW, SpeakToChatConfig.Timeout.STANDARD), "fc:0c:02:01"); + put(new SpeakToChatConfig(false, SpeakToChatConfig.Sensitivity.AUTO, SpeakToChatConfig.Timeout.STANDARD), "fc:0c:00:01"); + put(new SpeakToChatConfig(false, SpeakToChatConfig.Sensitivity.AUTO, SpeakToChatConfig.Timeout.SHORT), "fc:0c:00:00"); + put(new SpeakToChatConfig(false, SpeakToChatConfig.Sensitivity.AUTO, SpeakToChatConfig.Timeout.LONG), "fc:0c:00:02"); + put(new SpeakToChatConfig(false, SpeakToChatConfig.Sensitivity.AUTO, SpeakToChatConfig.Timeout.OFF), "fc:0c:00:03"); + put(new SpeakToChatConfig(false, SpeakToChatConfig.Sensitivity.AUTO, SpeakToChatConfig.Timeout.STANDARD), "fc:0c:00:01"); + }}); + } + @Test public void getQuickAccess() { final Request request = protocol.getQuickAccess(); @@ -86,6 +139,42 @@ public class SonyProtocolImplV3Test { } } + @Test + public void setPauseWhenTakenOff() { + assertRequests(protocol::setPauseWhenTakenOff, new LinkedHashMap() {{ + put(new PauseWhenTakenOff(false), "f8:01:01"); + put(new PauseWhenTakenOff(true), "f8:01:00"); + }}); + } + + @Test + public void setEqualizerCustomBands() { + assertRequests(protocol::setEqualizerCustomBands, new LinkedHashMap() {{ + put(new EqualizerCustomBands(Arrays.asList(0, 1, 2, 3, 1), 0), "58:00:a0:06:0a:0a:0b:0c:0d:0b"); + put(new EqualizerCustomBands(Arrays.asList(0, 1, 2, 3, 5), 0), "58:00:a0:06:0a:0a:0b:0c:0d:0f"); + put(new EqualizerCustomBands(Arrays.asList(0, 1, 2, 4, 5), 0), "58:00:a0:06:0a:0a:0b:0c:0e:0f"); + put(new EqualizerCustomBands(Arrays.asList(5, 1, 2, 3, 5), 0), "58:00:a0:06:0a:0f:0b:0c:0d:0f"); + put(new EqualizerCustomBands(Arrays.asList(0, 1, 2, 3, 5), -6), "58:00:a0:06:04:0a:0b:0c:0d:0f"); + put(new EqualizerCustomBands(Arrays.asList(0, 1, 2, 3, 5), 10), "58:00:a0:06:14:0a:0b:0c:0d:0f"); + }}); + } + + @Test + public void setAutomaticPowerOff() { + assertRequests(protocol::setAutomaticPowerOff, new LinkedHashMap() {{ + put(AutomaticPowerOff.OFF, "28:05:11:00"); + put(AutomaticPowerOff.WHEN_TAKEN_OFF, "28:05:10:00"); + }}); + } + + @Test + public void setVoiceNotifications() { + assertRequests(protocol::setVoiceNotifications, 0x0e, new LinkedHashMap() {{ + put(new VoiceNotifications(false), "48:01:01"); + put(new VoiceNotifications(true), "48:01:00"); + }}); + } + @Test public void handleQuickAccess() { final Map commands = new LinkedHashMap() {{