forked from sidilabs/galaxybuds-gnome-extension
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuds_battery.py
executable file
·198 lines (164 loc) · 5.55 KB
/
buds_battery.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python3
"""
A python script to get battery level from Samsung Galaxy Buds devices
"""
# License: MIT
# Author: @ThePBone
# 06/30/2020
import bluetooth
import sys
import argparse
import datetime
msg_debounce = ""
def print_result(string, timestamp):
global msg_debounce
if msg_debounce != string:
if timestamp:
print(string + "," +
datetime.datetime.now().strftime("%FT%T.%f")[:-4] + "Z")
else:
print(string)
msg_debounce = string
def parse_gnome(data, islegacy):
if data[0] != (0xFE if islegacy else 0xFD):
print("Invalid SOM")
exit(2)
if data[3] == 97:
state = data[10]
if not islegacy:
string = "{},{},{},{},{}".format(
(state & 240) >> 4, state & 15, data[6], data[7], data[11])
else:
string = "{},{}".format(data[6], data[7])
elif data[3] == 96:
state = data[9]
if not islegacy:
string = "{},{},{},{},{}".format(
(state & 240) >> 4, state & 15, data[5], data[6], data[10])
else:
string = "{},{}".format(data[5], data[6])
else:
return False
print(string)
return True
def parse_message(data, islegacy, timestamp):
if data[0] != (0xFE if islegacy else 0xFD):
print("Invalid SOM")
exit(2)
if data[3] == 97:
if not islegacy:
string = "{},{},{}".format(data[6], data[7], data[11])
else:
string = "{},{}".format(data[6], data[7])
elif data[3] == 96:
if not islegacy:
string = "{},{},{}".format(data[5], data[6], data[10])
else:
string = "{},{}".format(data[5], data[6])
else:
return False
print_result(string, timestamp)
return True
def parse_message_wear_status(data, islegacy, timestamp):
global msg_debounce
if data[0] != (0xFE if islegacy else 0xFD):
print("Invalid SOM")
exit(2)
if data[3] == 97:
state = data[10]
elif data[3] == 96:
state = data[9]
else:
return False
if not islegacy:
left = id_to_placement((state & 240) >> 4)
right = id_to_placement(state & 15)
string = "{},{}".format(left, right)
else:
if state == 0:
string = "None"
elif state == 1:
string = "Right"
elif state == 16:
string = "Left"
elif state == 17:
string = "Both"
else:
string = "Unknown"
print_result(string, timestamp)
return True
def id_to_placement(id):
if id == 0:
return "Disconnected"
elif id == 1:
return "Wearing"
elif id == 2:
return "Idle"
elif id == 3:
return "InCase"
elif id == 4:
return "InClosedCase"
def main():
parser = argparse.ArgumentParser(description='Read battery values of the Samsung Galaxy Buds, Buds+, Buds Live or Buds Pro'
'[Left, Right, Case (Buds+ or later)]')
parser.add_argument('mac', metavar='mac-address', type=str, nargs=1,
help='MAC-Address of your Buds')
parser.add_argument('-m', '--monitor',
action='store_true', help="Notify on change")
parser.add_argument('-t', '--timestamp', action='store_true',
help="Print timestamps")
parser.add_argument('-w', '--wearing-status',
action='store_true', help="Print wearing status instead")
parser.add_argument('-g', '--gnome', action='store_true',
help="Print battery values and wearing status to be used with the Gnome extension")
parser.add_argument('-v', '--verbose', action='store_true',
help="Print debug information")
args = parser.parse_args()
verbose = args.verbose
if verbose:
print("Checking device model...")
islegacy = "Galaxy Buds (" in str(bluetooth.lookup_name(args.mac[0]))
if verbose:
print(str(bluetooth.lookup_name(args.mac[0])))
print("Searching for RFCOMM interface...")
uuid = ("00001101-0000-1000-8000-00805F9B34FB" if not islegacy else "00001102-0000-1000-8000-00805f9b34fd")
service_matches = bluetooth.find_service(
uuid=uuid, address=str(args.mac[0]))
port = host = None
for match in service_matches:
target_name = ""
if isinstance(match["name"], str):
target_name = "GEARMANAGER"
else:
target_name = b"GEARMANAGER"
if target_name in match["name"]:
port = match["port"]
host = match["host"]
break
if port is None or host is None:
print("Couldn't find the proprietary RFCOMM service")
sys.exit(1)
if verbose:
print("RFCOMM interface found. Establishing connection...")
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.connect((host, port))
if verbose:
print("Connected. Waiting for incoming data...")
try:
while True:
data = sock.recv(1024)
if len(data) == 0:
break
if args.wearing_status:
success = parse_message_wear_status(
data, islegacy, args.timestamp)
elif args.gnome:
success = parse_gnome(data, islegacy)
else:
success = parse_message(data, islegacy, args.timestamp)
if success and not args.monitor:
exit(0)
except IOError:
pass
if __name__ == "__main__":
main()