简体   繁体   中英

How to detect Bluetooth event in Linux with Python that is not submitted to dbus?

I'm writing a media player in Python that can play music in connected Bluetooth speakers. I got a speaker with 3 control buttons volume up/down, play/pause. My code (using mpv ) is playing music fine, the volume controls are working too. But I could not get the play/pause button working, here is what I found:

sudo evtest sees the event:

Testing ... (interrupt to exit)
Event: time 1661208706.167140, type 1 (EV_KEY), code 200 (KEY_PLAYCD), value 1
Event: time 1661208706.167140, -------------- SYN_REPORT ------------
Event: time 1661208706.255603, type 1 (EV_KEY), code 200 (KEY_PLAYCD), value 0
Event: time 1661208706.255603, -------------- SYN_REPORT ------------

sudo btmon sees them too:

< ACL Data TX: Handle 11 flags 0x00 dlen 12                                                                  #2575 [hci0] 124.622157
      Channel: 66 len 8 [PSM 23 mode Basic (0x00)] {chan 2}
      AVCTP Control: Response: type 0x00 label 2 PID 0x110e
        AV/C: Accepted: address 0x48 opcode 0x7c
          Subunit: Panel
          Opcode: Passthrough
          Operation: 0x44 (PLAY Pressed)
          Length: 0x00
> ACL Data RX: Handle 11 flags 0x02 dlen 12                                                                  #2576 [hci0] 124.623006
      Channel: 66 len 8 [PSM 23 mode Basic (0x00)] {chan 2}
      AVCTP Control: Command: type 0x00 label 3 PID 0x110e
        AV/C: Control: address 0x48 opcode 0x7c
          Subunit: Panel
          Opcode: Passthrough
          Operation: 0xc4 (PLAY Released)
          Length: 0x00

dbus-monitor --system displays the volume change events, but does nothing when I press the "play" button.

How could I catch these events in a python program? I can access dbus events with dbus-python , but the play event does not get submitted there.

As @ukBaz mentioned the trick is that the play button behaves as a connected keyboard key and their events are not dispatched to the dbus. On my Pi it creates a new device at /dev/input/event1 (but note that its physical address is not the same as the bluetooth address of the connected device. Also its created only after a while BT is connected, maybe when music starts playing?)

From here I can read the events with evdev with another catch: KEY_PAUSECD is dispatched if music is playing and KEY_PLAYCD if not. KEY_PAUSECD just dispatched once when music is playing. This is the relevant code:

LinuxEventProcessor.thread = threading.Thread(target = LinuxEventProcessor.evdev_handler, name="evdev", daemon=True)
LinuxEventProcessor.thread.start()

def evdev_handler():
    # TODO get the right device from the physical address. The issue is that this address does not
    # equal the BT device's MAC address.
    device = evdev.InputDevice('/dev/input/event1')
    for event in device.read_loop():
        print("BT input event:", evdev.categorize(event))
        if event.type == evdev.ecodes.EV_KEY and event.value == 1:
            if event.code == 201: # KEY_PAUSECD. Dispatched only once when music is playing
                radio_player.RadioPlayer.stop_radio()
            if event.code == 200: # KEY_PLAYCD
                radio_player.RadioPlayer.play_radio_if_bt_connected()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM