Control your Logitech Z407 Bluetooth speakers from Home Assistant using an ESP32 and ESPHome.
This custom ESPHome component allows you to control Logitech Z407 speakers via Bluetooth Low Energy (BLE). It provides full control over volume, bass, input selection, and playback functions, integrating seamlessly with Home Assistant.
My repo is full of years of python with a smattering of GO. However, I have done precisely zero C++. This was human-in-the-loop with Cursor.AI using Claude Sonnet, but aside from testing, I wrote zero lines of the cpp code.
- ✅ Volume Control - Increase/decrease master volume
- ✅ Bass Control - Adjust bass levels independently
- ✅ Input Switching - Switch between Bluetooth, AUX, and USB inputs
- ✅ Playback Control - Play/pause, next/previous track
- ✅ Connection Status - Monitor BLE connection state
- ✅ Auto-Reconnect - Automatically reconnects after disconnection
- ✅ Discovery Mode - Find your Z407's MAC address without external tools
- ✅ State Tracking - Tracks current input source
- ✅ Home Assistant Integration - Native entities (buttons, select, sensors)
- ESP32 Development Board (ESP32-DevKitC or similar)
- Logitech Z407 Speakers
- USB Power Supply for ESP32
- Any ESP32 board with BLE support
Note: ESP8266 is not supported (no BLE hardware).
First, you need to find your Z407's MAC address:
-
Prepare the Z407:
- Remove batteries from the physical Z407 remote (or ensure it's disconnected)
- Ensure no other device is connected to the Z407
- The Z407 should be powered on and advertising
-
Flash discovery configuration:
esphome run examples/z407_discovery.yaml
-
Find the MAC address:
- Check the logs or Home Assistant for "Discovered Z407 Address"
- Note down the MAC address (format:
AA:BB:CC:DD:EE:FF)
-
Update configuration:
- Copy
examples/z407_basic.yamlto your ESPHome directory - Replace
z407_macwith your discovered MAC address - Update WiFi credentials in
secrets.yaml
- Copy
-
Flash the configuration:
esphome run z407_basic.yaml
-
Add to Home Assistant:
- The device should auto-discover via ESPHome API
- All entities will appear in Home Assistant
Add to your ESPHome YAML:
external_components:
- source: github://stratus-ss/logitech407-esphome
components: [z407_controller]- Copy the
components/z407_controllerdirectory to your ESPHomecustom_componentsfolder - Reference it in your YAML:
external_components:
- source: custom_components
components: [z407_controller]For a detailed understanding of how this component works, including code flow diagrams and state machines, see the Code Flow Documentation.
esp32:
board: esp32dev
framework:
type: esp-idf # Required for stable BLE
esp32_ble_tracker:
scan_parameters:
active: true
ble_client:
- mac_address: "AA:BB:CC:DD:EE:FF"
id: z407_ble_client
z407_controller:
id: z407
ble_client_id: z407_ble_client| Option | Type | Default | Description |
|---|---|---|---|
id |
ID | Required | Component ID |
ble_client_id |
ID | Required* | BLE client to use |
discovery_mode |
boolean | false |
Enable discovery mode |
discovery_duration |
time | 30s |
How long to scan in discovery mode |
connection_timeout |
time | 30s |
Connection timeout during handshake |
auto_reconnect |
boolean | true |
Auto-reconnect on disconnection |
*Either ble_client_id or discovery_mode must be set, but not both.
All Z407 commands are available as buttons:
button:
- platform: z407_controller
z407_controller_id: z407
volume_up:
name: "Volume Up"
volume_down:
name: "Volume Down"
bass_up:
name: "Bass Up"
bass_down:
name: "Bass Down"
play_pause:
name: "Play/Pause"
next_track:
name: "Next Track"
previous_track:
name: "Previous Track"
input_bluetooth:
name: "Switch to Bluetooth"
input_aux:
name: "Switch to AUX"
input_usb:
name: "Switch to USB"
bluetooth_pair:
name: "Bluetooth Pairing Mode"
factory_reset:
name: "Factory Reset"
sound_1:
name: "Sound 1"
sound_2:
name: "Sound 2"
sound_3:
name: "Sound 3"Input source selector:
select:
- platform: z407_controller
z407_controller_id: z407
input_source:
name: "Input Source"Options: Bluetooth, AUX, USB
Connection status:
binary_sensor:
- platform: z407_controller
z407_controller_id: z407
connection:
name: "Connected"
device_class: connectivitySignal strength (RSSI):
sensor:
- platform: z407_controller
z407_controller_id: z407
rssi:
name: "Signal Strength"
update_interval: 60sNote: RSSI functionality is temporarily disabled pending ESPHome BLE API updates. The sensor will be present but will not report values.
Discovery results (discovery mode only):
text_sensor:
- platform: z407_controller
z407_controller_id: z407
discovered_address:
name: "Discovered Z407 Address"- Service UUID:
0000fdc2-0000-1000-8000-00805f9b34fb - Command Characteristic:
c2e758b9-0e78-41e0-b0cb-98a593193fc5(write, no response) - Response Characteristic:
b84ac9c6-29c5-46d4-bba1-9d534784330f(notify)
- Connect to BLE device
- Subscribe to response characteristic
- Send
0x8405(Pairing Initiate) - Wait for
0xD40501response - Send
0x8400(Pairing Acknowledge) - Wait for
0xD40001response (optional - may be skipped) - Wait for
0xD40003(Connected) - Ready for commands
Note: The Z407 may skip step 6 and send 0xD40003 directly after 0x8400. The component handles both sequences.
| Command | Hex Code | Description |
|---|---|---|
| Bass Up | 0x8000 |
Increase bass level |
| Bass Down | 0x8001 |
Decrease bass level |
| Volume Up | 0x8002 |
Increase master volume |
| Volume Down | 0x8003 |
Decrease master volume |
| Play/Pause | 0x8004 |
Toggle playback |
| Next Track | 0x8005 |
Skip to next track |
| Previous Track | 0x8006 |
Go to previous track |
| Input Bluetooth | 0x8101 |
Switch to Bluetooth input |
| Input AUX | 0x8102 |
Switch to 3.5mm AUX input |
| Input USB | 0x8103 |
Switch to USB input |
| Bluetooth Pair | 0x8200 |
Enter BT pairing mode |
| Factory Reset | 0x8300 |
Reset to factory defaults |
| Sound 1 | 0x8501 |
Play failure/disconnect chime |
| Sound 2 | 0x8502 |
Play mode switch chime |
| Sound 3 | 0x8503 |
Play connection chime |
| Response | Hex Code | Meaning |
|---|---|---|
| Initiate Response | 0xD40501 |
Handshake step 1 OK |
| Acknowledge Response | 0xD40001 |
Handshake step 2 OK |
| Connected | 0xD40003 |
Handshake complete |
| Command Confirmation | 0xC0XX |
Command received |
| Status Update | 0xCF04 |
Switched to Bluetooth |
| Status Update | 0xCF05 |
Switched to AUX |
| Status Update | 0xCF06 |
Switched to USB |
Example automation to auto-switch input:
automation:
- alias: "Switch Z407 to Bluetooth when phone connects"
trigger:
- platform: state
entity_id: device_tracker.phone
to: "home"
action:
- service: button.press
target:
entity_id: button.z407_input_bluetoothESP32 shares the 2.4GHz antenna between BLE and WiFi. For best performance:
- Use
fast_connect: truein WiFi config - Avoid heavy WiFi traffic during BLE operations
- Consider disabling WiFi power saving
- Use
esp-idfframework (better BLE stack)
Commands are rate-limited to 100ms minimum interval to prevent overwhelming the Z407. If you need to send multiple commands, add delays:
script:
- id: multi_command
then:
- button.press: volume_up
- delay: 150ms
- button.press: bass_upSymptoms: Logs show Cannot send command - not ready
Solution:
- Check ESP32 logs for handshake completion:
[I][z407_controller:123] Handshake complete - connection established! - Ensure the physical Z407 remote is unpaired/disconnected
- Verify only one device is connected to the Z407 at a time
- Try power cycling both the Z407 and ESP32
- Enable debug logging to see detailed handshake sequence:
logger: level: DEBUG
Symptoms: Device won't connect or disconnects frequently
Solution:
- Move ESP32 closer to Z407 (BLE range is shorter than WiFi)
- Reduce WiFi interference:
wifi: fast_connect: true power_save_mode: none
- Use ESP-IDF framework (more stable BLE):
esp32: framework: type: esp-idf
- Check that Z407 is powered on and not in pairing mode with another device
Symptoms: No devices discovered in discovery mode
Solution:
- Ensure Z407 is powered on
- Remove batteries from physical Z407 remote
- Disconnect any Bluetooth devices connected to Z407
- Verify Z407 is advertising (LED should be flashing)
- Increase discovery duration:
z407_controller: discovery_mode: true discovery_duration: 60s
Enable detailed logging for troubleshooting:
logger:
level: DEBUG
logs:
z407_controller: DEBUG
ble_client: DEBUG
esp32_ble: DEBUGThis will show:
- Handshake sequence details
- Command send/receive status
- BLE connection state changes
- Characteristic handles
- All notifications from Z407
This project is based on the reverse engineering work by freundTech. Without their protocol documentation, this component would not exist.
This project is licensed under the AGPLv3 License - see the LICENSE file for details.
This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with Logitech International S.A., or any of its subsidiaries or its affiliates.
Known Issues:
- RSSI sensor temporarily disabled pending ESPHome BLE API updates
Testing:
- Verified compilation with ESPHome 2025.11.x
- Tested handshake sequence with real Z407 hardware
- Validated command execution and state tracking
- Full Z407 protocol implementation
- All commands supported
- Discovery mode
- Auto-reconnect
- State tracking
- Home Assistant integration
- Comprehensive documentation