Skip to content

Commit 8dc20f4

Browse files
committed
Add HID consumer controls
1 parent 9473d73 commit 8dc20f4

File tree

4 files changed

+111
-40
lines changed

4 files changed

+111
-40
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,50 @@ bluetooth = bluefang.Bluefang()
8484
bluetooth.register_profile("/omnihub/profile")
8585
bluetooth.scan(5000)
8686
bluetooth.connect("D0:03:4B:24:57:84")
87+
```
88+
89+
# HID Descriptor
90+
```
91+
// 78 bytes
92+
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
93+
0x09, 0x06, // Usage (Keyboard)
94+
0xA1, 0x01, // Collection (Application)
95+
0x85, 0x01, // Report ID (1)
96+
0x05, 0x07, // Usage Page (Kbrd/Keypad)
97+
0x75, 0x01, // Report Size (1)
98+
0x95, 0x08, // Report Count (8)
99+
0x19, 0xE0, // Usage Minimum (0xE0)
100+
0x29, 0xE7, // Usage Maximum (0xE7)
101+
0x15, 0x00, // Logical Minimum (0)
102+
0x25, 0x01, // Logical Maximum (1)
103+
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
104+
0x95, 0x03, // Report Count (1)
105+
0x75, 0x08, // Report Size (8)
106+
0x15, 0x00, // Logical Minimum (0)
107+
0x25, 0x64, // Logical Maximum (100)
108+
0x05, 0x07, // Usage Page (Kbrd/Keypad)
109+
0x19, 0x00, // Usage Minimum (0x00)
110+
0x29, 0x65, // Usage Maximum (0x65)
111+
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
112+
0xC0, // End Collection
113+
0x05, 0x0C, // Usage Page (Consumer)
114+
0x09, 0x01, // Usage (Consumer Control)
115+
0xA1, 0x01, // Collection (Application)
116+
0x85, 0x03, // Report ID (3)
117+
0x05, 0x0C, // Usage Page (Consumer)
118+
0x15, 0x00, // Logical Minimum (0)
119+
0x25, 0x01, // Logical Maximum (1)
120+
0x75, 0x01, // Report Size (1)
121+
0x95, 0x07, // Report Count (7)
122+
0x09, 0xb8, // Usage (Eject)
123+
0x09, 0xb6, // Usage (Scan Previous Track)
124+
0x09, 0xcd, // Usage (Play/Pause)
125+
0x09, 0xb5, // Usage (Scan Next Track)
126+
0x09, 0xe2, // Usage (Mute)
127+
0x09, 0xea, // Usage (Volume Decrement)
128+
0x09, 0xe9, // Usage (Volume Increment)
129+
0x09, 0x30, // Usage (Power)
130+
0x09, 0xB0, // Usage (Play)
131+
0x09, 0xB1, // Usage (Pause)
132+
0xC0, // End Collection
87133
```

bluefang/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
LEFT = "left"
66
RIGHT = "right"
77
PLAY = "play"
8+
PAUSE = "pause"
89
PREV_TRACK = "prev_track"
910
NEXT_TRACK = "next_track"
1011
MUTE_VOLUME = "mute_volume"

bluefang/l2cap.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from queue import *
55
from threading import Thread
66

7+
from bluefang import commands
8+
79
q = Queue()
810

911
class L2CAPClientThread(Thread):
@@ -21,24 +23,47 @@ def run(self):
2123

2224
def process_command(self, command):
2325
logging.info("Received command: " + command)
24-
if command == "left":
26+
# Keyboard Reports
27+
if command == commands.LEFT:
2528
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00))))
2629
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
27-
elif command == "right":
30+
elif command == commands.RIGHT:
2831
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00))))
2932
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
30-
elif command == "up":
33+
elif command == commands.UP:
3134
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00))))
3235
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
33-
elif command == "down":
36+
elif command == commands.DOWN:
3437
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00))))
3538
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
36-
elif command == "enter":
39+
elif command == commands.ENTER:
3740
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00))))
3841
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
39-
elif command == "cancel":
42+
elif command == commands.CANCEL:
4043
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00))))
4144
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
45+
elif command == commands.MUTE_VOLUME:
46+
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00))))
47+
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
48+
elif command == commands.VOLUME_UP:
49+
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00))))
50+
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
51+
elif command == commands.VOLUME_DOWN:
52+
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00))))
53+
self.socket.send(bytes(bytearray((0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
54+
# Consumer Reports
55+
elif command == commands.PLAY:
56+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
57+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
58+
elif command == commands.PAUSE:
59+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
60+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
61+
elif command == commands.NEXT_TRACK:
62+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
63+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
64+
elif command == commands.PREV_TRACK:
65+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
66+
self.socket.send(bytes(bytearray((0xA1, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))))
4267
else:
4368
logging.warning("Unknown command: " + command)
4469

bluefang/servicerecords.py

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,48 @@
44
<?xml version="1.0" encoding="UTF-8" ?>
55
66
<record>
7-
<attribute id="0x0001">
7+
<attribute id="0x0001"> <!-- ServiceClassIDList -->
88
<sequence>
99
<uuid value="0x1124" />
1010
</sequence>
1111
</attribute>
12-
<attribute id="0x0004">
12+
<attribute id="0x0004"> <!-- ProtocolDescriptorList -->
1313
<sequence>
1414
<sequence>
15-
<uuid value="0x0100" />
15+
<uuid value="0x0100" /> <!-- L2CAP -->
1616
<uint16 value="0x0011" />
1717
</sequence>
1818
<sequence>
1919
<uuid value="0x0011" />
2020
</sequence>
2121
</sequence>
2222
</attribute>
23-
<attribute id="0x0005">
23+
<attribute id="0x0005"> <!-- BrowseGroupList -->
2424
<sequence>
2525
<uuid value="0x1002" />
2626
</sequence>
2727
</attribute>
28-
<attribute id="0x0006">
28+
<attribute id="0x0006"> <!-- LanguageBaseAttributeIDList -->
2929
<sequence>
30-
<uint16 value="0x656e" />
31-
<uint16 value="0x006a" />
32-
<uint16 value="0x0100" />
30+
<uint16 value="0x656e" /> <!-- en- (English) -->
31+
<uint16 value="0x006a" /> <!-- UTF-8 encoding -->
32+
<uint16 value="0x0100" /> <!-- PrimaryLanguageBaseId = 0 -->
3333
</sequence>
3434
</attribute>
35-
<attribute id="0x0009">
35+
<attribute id="0x0009"> <!-- Bluetooth Profile Descriptor List -->
3636
<sequence>
3737
<sequence>
38-
<uuid value="0x1124" />
39-
<uint16 value="0x0100" />
38+
<uuid value="0x1124" /> <!-- HID Profile -->
39+
<uint16 value="0x0100" /> <!-- L2CAP -->
4040
</sequence>
4141
</sequence>
4242
</attribute>
43-
<attribute id="0x000d">
43+
<attribute id="0x000d"> <!-- AdditionalProtocolDescriptorLists -->
4444
<sequence>
4545
<sequence>
4646
<sequence>
47-
<uuid value="0x0100" />
48-
<uint16 value="0x0013" />
47+
<uuid value="0x0100" /> <!-- L2CAP -->
48+
<uint16 value="0x0013" /> <!-- PSM for HID Interrupt -->
4949
</sequence>
5050
<sequence>
5151
<uuid value="0x0011" />
@@ -62,66 +62,65 @@
6262
<attribute id="0x0102">
6363
<text value="Raspberry Pi" />
6464
</attribute>
65-
<attribute id="0x0200">
65+
<attribute id="0x0200"> <!-- HIDDeviceReleaseNumber (Deprecated) -->
6666
<uint16 value="0x0100" />
6767
</attribute>
68-
<attribute id="0x0201">
69-
<uint16 value="0x0111" />
68+
<attribute id="0x0201"> <!-- HIDParserVersion -->
69+
<uint16 value="0x0111" />
7070
</attribute>
71-
<attribute id="0x0202">
72-
<!--<uint8 value="0x40" />-->
71+
<attribute id="0x0202"> <!-- HIDDeviceSubclass -->
7372
<uint8 value="0x4c" />
7473
</attribute>
75-
<attribute id="0x0203">
74+
<attribute id="0x0203"> <!-- HIDCountryCode -->
7675
<uint8 value="0x00" />
7776
</attribute>
78-
<attribute id="0x0204">
77+
<attribute id="0x0204"> <!-- HIDVirtualCable -->
7978
<boolean value="true" />
8079
</attribute>
81-
<attribute id="0x0205">
80+
<attribute id="0x0205"> <!-- HIDRecoonectInitiate -->
8281
<boolean value="true" />
8382
</attribute>
84-
<attribute id="0x0206">
83+
<attribute id="0x0206"> <!-- HIDDescriptorList -->
8584
<sequence>
8685
<sequence>
8786
<uint8 value="0x22" />
88-
<text encoding="hex" value="05010906a101850175019508050719e029e715002501810295017508810395057501050819012905910295017503910395067508150026ff000507190029ff8100c0050c0901a1018503150025017501950b0a23020a21020ab10109b809b609cd09b509e209ea09e9093081029501750d8103c0" />
87+
<text encoding="hex" value="05010906a101850175019508050719e029e715002501810295017508810395057501050819012905910295017503910395067508150026ff000507190029ff8100c0050c0901a1018503150025017501950b0a23020a21020ab10109b809b609cd09b509e209ea09e9093009B009B181029501750d8103c0" />
8988
</sequence>
9089
</sequence>
9190
</attribute>
92-
<attribute id="0x0207">
91+
<attribute id="0x0207"> <!-- HIDLANGIDBaseList -->
9392
<sequence>
9493
<sequence>
9594
<uint16 value="0x0409" />
9695
<uint16 value="0x0100" />
9796
</sequence>
9897
</sequence>
9998
</attribute>
100-
<attribute id="0x0208">
99+
<attribute id="0x0208"> <!-- HIDSDPDisable (Deprecated) -->
101100
<boolean value="true" />
102101
</attribute>
103-
<attribute id="0x0209">
102+
<attribute id="0x0209"> <!-- HIDBatteryPower -->
104103
<boolean value="false" />
105104
</attribute>
106-
<attribute id="0x020a">
105+
<attribute id="0x020a"> <!-- HIDRemoteWake -->
107106
<boolean value="true" />
108107
</attribute>
109-
<attribute id="0x020b">
108+
<attribute id="0x020b"> <!-- HIDProfileVersion -->
110109
<uint16 value="0x1124" />
111110
</attribute>
112-
<attribute id="0x020c">
111+
<attribute id="0x020c"> <!-- HIDSupervisionTimeout -->
113112
<uint16 value="0x0c80" />
114113
</attribute>
115-
<attribute id="0x020d">
114+
<attribute id="0x020d"> <!-- HIDNormallyConnectable -->
116115
<boolean value="false" />
117116
</attribute>
118-
<attribute id="0x020e">
117+
<attribute id="0x020e"> <!-- HIDBootDevice -->
119118
<boolean value="true" />
120119
</attribute>
121-
<attribute id="0x020f">
120+
<attribute id="0x020f"> <!-- HIDSSRHostMaxLatency -->
122121
<uint16 value="0x0640" />
123122
</attribute>
124-
<attribute id="0x0210">
123+
<attribute id="0x0210"> <!-- HIDSSRHostMinTimeout -->
125124
<uint16 value="0x0320" />
126125
</attribute>
127126
</record>

0 commit comments

Comments
 (0)