From 1919c1898e25122c92a6560ae07e3ad61cedfe43 Mon Sep 17 00:00:00 2001 From: Sniqi Date: Sat, 4 Apr 2026 03:27:42 +0200 Subject: [PATCH] Add support for Sony INZONE Buds (WF-G700N) Adds battery status reading for the Sony INZONE Buds wireless gaming earbuds via their USB dongle (VID 054c PID 0ec2). The dongle sends unsolicited HID reports containing battery levels for the right earbud, left earbud, and charging case. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 1 + lib/device_registry.cpp | 6 +++ lib/devices/sony_inzone_buds.hpp | 80 ++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 lib/devices/sony_inzone_buds.hpp diff --git a/README.md b/README.md index b723aca..2ae3232 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ A cross-platform tool to control USB gaming headsets on **Linux**, **macOS**, an | ROCCAT Elo 7.1 USB | All | | | | x | | | | | | | | | | | | | | Audeze Maxwell | All | x | x | | | x | x | x | | x | | | | | x | | | | Lenovo Wireless VoIP Headset | All | x | x | | | x | | x | x | x | | | | | x | | | +| Sony INZONE Buds | All | | x | | | | | | | | | | | | | | | | HeadsetControl Test device | All | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | **Platform:** All = Linux, macOS, Windows | L/M = Linux and macOS only diff --git a/lib/device_registry.cpp b/lib/device_registry.cpp index 18fdbc7..11c8473 100644 --- a/lib/device_registry.cpp +++ b/lib/device_registry.cpp @@ -44,6 +44,9 @@ // Lenovo devices #include "devices/lenovo_wireless_voip.hpp" +// Sony devices +#include "devices/sony_inzone_buds.hpp" + // Test device #include "devices/headsetcontrol_test.hpp" @@ -129,6 +132,9 @@ void DeviceRegistry::initialize() // Lenovo devices registerDevice(std::make_unique()); + // Sony devices + registerDevice(std::make_unique()); + // Test device registerDevice(std::make_unique()); }); diff --git a/lib/devices/sony_inzone_buds.hpp b/lib/devices/sony_inzone_buds.hpp new file mode 100644 index 0000000..94c6f3d --- /dev/null +++ b/lib/devices/sony_inzone_buds.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include "../result_types.hpp" +#include "hid_device.hpp" +#include +#include + +using namespace std::string_view_literals; + +namespace headsetcontrol { + +/** + * @brief Sony INZONE Buds (WF-G700N) wireless gaming earbuds + * + * Communicates via USB dongle (VID 054c PID 0ec2). + * The dongle sends unsolicited HID reports; battery data is in the report + * identified by byte[1]=0x12 and byte[2]=0x04 (report ID byte[0]=0x02). + * + * Battery bytes (0-100, direct percentage): + * byte[14] = right earbud + * byte[16] = left earbud + * byte[18] = charging case + * + * Checksum: byte[19] = (byte[14] + byte[16] + 117) mod 256 + */ +class SonyINZONEBuds : public HIDDevice { +public: + static constexpr uint16_t VENDOR_SONY = 0x054c; + static constexpr std::array PRODUCT_IDS { 0x0ec2 }; + + static constexpr int REPORT_SIZE = 64; + static constexpr uint8_t BATTERY_TYPE = 0x12; + static constexpr uint8_t BATTERY_SUBTYPE = 0x04; + static constexpr int MAX_READ_ATTEMPTS = 45; + + static constexpr int BYTE_RIGHT_EARBUD = 14; + static constexpr int BYTE_LEFT_EARBUD = 16; + static constexpr int BYTE_CASE = 18; + + constexpr uint16_t getVendorId() const override { return VENDOR_SONY; } + + std::vector getProductIds() const override + { + return { PRODUCT_IDS.begin(), PRODUCT_IDS.end() }; + } + + std::string_view getDeviceName() const override { return "Sony INZONE Buds"sv; } + + constexpr int getCapabilities() const override { return B(CAP_BATTERY_STATUS); } + + Result getBattery(hid_device* device_handle) override + { + std::array response {}; + + for (int attempt = 0; attempt < MAX_READ_ATTEMPTS; ++attempt) { + auto rd = readHIDTimeout(device_handle, response, hsc_device_timeout); + if (!rd) { + if (rd.error().code == DeviceError::Code::Timeout) + continue; + return rd.error(); + } + + if (response[1] == BATTERY_TYPE && response[2] == BATTERY_SUBTYPE) { + uint8_t right = response[BYTE_RIGHT_EARBUD]; + uint8_t left = response[BYTE_LEFT_EARBUD]; + + return BatteryResult { + .level_percent = std::min(left, right), + .status = BATTERY_AVAILABLE, + .raw_data + = std::vector(response.begin(), response.end()), + }; + } + } + + return DeviceError::timeout("No battery report received within attempt limit"); + } +}; + +} // namespace headsetcontrol