Skip to content

Commit 689256d

Browse files
committed
Get connection info from D-Bus API
There are device properties for RSSI and Tx power now and link quality is pointless.
1 parent f946bc6 commit 689256d

File tree

5 files changed

+13
-267
lines changed

5 files changed

+13
-267
lines changed

blueman/gui/manager/ManagerDeviceList.py

+13-75
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from gettext import gettext as _
2-
from typing import Optional, TYPE_CHECKING, List, Any, cast, Callable, Set, Dict
2+
from typing import Optional, TYPE_CHECKING, List, Any, cast, Callable, Dict
33
import html
44
import logging
55
import cairo
@@ -16,12 +16,10 @@
1616
from blueman.Functions import launch
1717
from blueman.Sdp import ServiceUUID, OBEX_OBJPUSH_SVCLASS_ID
1818
from blueman.gui.GtkAnimation import TreeRowFade, CellFade, AnimBase
19-
from _blueman import ConnInfoReadError, conn_info
2019

2120
import gi
2221
gi.require_version("Gtk", "3.0")
2322
from gi.repository import Gtk
24-
from gi.repository import GLib
2523
from gi.repository import Gio
2624
from gi.repository import Gdk
2725
from gi.repository import GdkPixbuf
@@ -71,8 +69,6 @@ def __init__(self, inst: "Blueman", adapter: Optional[str] = None) -> None:
7169
self.props.has_tooltip = True
7270
self.Blueman = inst
7371

74-
self._monitored_devices: Set[str] = set()
75-
7672
self.manager.connect_signal("battery-created", self.on_battery_created)
7773
self.manager.connect_signal("battery-removed", self.on_battery_removed)
7874
self._batteries: Dict[str, Battery] = {}
@@ -368,48 +364,6 @@ def row_setup_event(self, tree_iter: Gtk.TreeIter, device: Device) -> None:
368364
except Exception as e:
369365
logging.exception(e)
370366

371-
if device["Connected"]:
372-
self._monitor_power_levels(tree_iter, device)
373-
374-
def _monitor_power_levels(self, tree_iter: Gtk.TreeIter, device: Device) -> None:
375-
if device["Address"] in self._monitored_devices:
376-
return
377-
378-
assert self.Adapter is not None
379-
cinfo = conn_info(device["Address"], os.path.basename(self.Adapter.get_object_path()))
380-
try:
381-
cinfo.init()
382-
except ConnInfoReadError:
383-
logging.warning("Failed to get power levels, probably a LE device.")
384-
385-
model = self.liststore
386-
assert isinstance(model, Gtk.TreeModel)
387-
r = Gtk.TreeRowReference.new(model, model.get_path(tree_iter))
388-
self._update_power_levels(tree_iter, device, cinfo)
389-
GLib.timeout_add(1000, self._check_power_levels, r, cinfo, device["Address"])
390-
self._monitored_devices.add(device["Address"])
391-
392-
def _check_power_levels(self, row_ref: Gtk.TreeRowReference, cinfo: conn_info, address: str) -> bool:
393-
if not row_ref.valid():
394-
logging.warning("stopping monitor (row does not exist)")
395-
cinfo.deinit()
396-
self._monitored_devices.remove(address)
397-
return False
398-
399-
tree_iter = self.get_iter(row_ref.get_path())
400-
assert tree_iter is not None
401-
402-
device = self.get(tree_iter, "device")["device"]
403-
404-
if device["Connected"]:
405-
self._update_power_levels(tree_iter, device, cinfo)
406-
return True
407-
else:
408-
cinfo.deinit()
409-
self._disable_power_levels(tree_iter)
410-
self._monitored_devices.remove(address)
411-
return False
412-
413367
def row_update_event(self, tree_iter: Gtk.TreeIter, key: str, value: Any) -> None:
414368
logging.info(f"{key} {value}")
415369

@@ -438,9 +392,7 @@ def row_update_event(self, tree_iter: Gtk.TreeIter, key: str, value: Any) -> Non
438392
elif key == "Connected":
439393
self.set(tree_iter, connected=value)
440394

441-
if value:
442-
self._monitor_power_levels(tree_iter, self.get(tree_iter, "device")["device"])
443-
else:
395+
if not value:
444396
self._disable_power_levels(tree_iter)
445397
elif key == "Name":
446398
self.set(tree_iter, no_name=False)
@@ -449,40 +401,26 @@ def row_update_event(self, tree_iter: Gtk.TreeIter, key: str, value: Any) -> Non
449401
elif key == "Blocked":
450402
self.set(tree_iter, blocked=value)
451403

452-
def _update_power_levels(self, tree_iter: Gtk.TreeIter, device: Device, cinfo: conn_info) -> None:
453-
row = self.get(tree_iter, "cell_fader", "battery", "rssi", "lq", "tpl")
454-
455-
bars = {}
404+
elif key == "RSSI":
405+
self._update_bar(tree_iter, "rssi", 50 if value is None else max(50 + float(value) / 127 * 50, 10))
456406

457-
obj_path = device.get_object_path()
458-
if obj_path in self._batteries:
459-
bars["battery"] = self._batteries[obj_path]["Percentage"]
407+
elif key == "TxPower":
408+
self._update_bar(tree_iter, "tpl", 0 if value is None else max(float(value) / 127 * 100, 10))
460409

461-
# cinfo init may fail for bluetooth devices version 4 and up
462-
# FIXME Workaround is horrible and we should show something better
463-
if cinfo.failed:
464-
bars.update({"rssi": 100.0, "tpl": 100.0})
465-
else:
466-
try:
467-
bars["rssi"] = max(50 + float(cinfo.get_rssi()) / 127 * 50, 10)
468-
except ConnInfoReadError:
469-
bars["rssi"] = 50
470-
try:
471-
bars["tpl"] = max(50 + float(cinfo.get_tpl()) / 127 * 50, 10)
472-
except ConnInfoReadError:
473-
bars["tpl"] = 50
410+
def _update_bar(self, tree_iter: Gtk.TreeIter, name: str, perc: float) -> None:
411+
row = self.get(tree_iter, "cell_fader", "battery", "rssi", "tpl")
412+
row[name] = perc
474413

475414
if row["battery"] == row["rssi"] == row["tpl"] == 0:
476415
self._prepare_fader(row["cell_fader"]).animate(start=0.0, end=1.0, duration=400)
477416

478417
w = 14 * self.get_scale_factor()
479418
h = 48 * self.get_scale_factor()
480419

481-
for (name, perc) in bars.items():
482-
if round(row[name], -1) != round(perc, -1):
483-
icon_name = f"blueman-{name}-{int(round(perc, -1))}.png"
484-
icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(os.path.join(PIXMAP_PATH, icon_name), w, h, True)
485-
self.set(tree_iter, **{name: perc, f"{name}_pb": icon})
420+
if round(row[name], -1) != round(perc, -1):
421+
icon_name = f"blueman-{name}-{int(round(perc, -1))}.png"
422+
icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(os.path.join(PIXMAP_PATH, icon_name), w, h, True)
423+
self.set(tree_iter, **{name: perc, f"{name}_pb": icon})
486424

487425
def _disable_power_levels(self, tree_iter: Gtk.TreeIter) -> None:
488426
row = self.get(tree_iter, "cell_fader", "battery", "rssi", "tpl")

module/_blueman.pyx

-63
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,6 @@ cdef extern from "bluetooth/rfcomm.h":
7272

7373

7474
cdef extern from "libblueman.h":
75-
cdef struct conn_info_handles:
76-
unsigned int handle
77-
int dd
78-
79-
80-
cdef int connection_init(int dev_id, char *addr, conn_info_handles *ci)
81-
cdef int connection_get_rssi(conn_info_handles *ci, signed char *ret_rssi)
82-
cdef int connection_get_tpl(conn_info_handles *ci, signed char *ret_tpl, unsigned char type)
83-
cdef int connection_close(conn_info_handles *ci)
8475
cdef int c_get_rfcomm_channel "get_rfcomm_channel" (unsigned short service_class, char* btd_addr)
8576
cdef int get_rfcomm_list(rfcomm_dev_list_req **ret)
8677
cdef int c_create_rfcomm_device "create_rfcomm_device" (char *local_address, char *remote_address, int channel)
@@ -94,11 +85,6 @@ class RFCOMMError(Exception):
9485

9586
ERR = {
9687
-1:"Can't allocate memory",
97-
-2:"HCI device open failed",
98-
-3:"Not connected",
99-
-4:"Get connection info failed",
100-
-5:"Read RSSI failed",
101-
-6:"Read transmit power level request failed",
10288
-8:"Getting rfcomm list failed",
10389
-9:"ERR_SOCKET_FAILED",
10490
-12: "Can't bind RFCOMM socket",
@@ -205,55 +191,6 @@ def destroy_bridge(py_name="pan1"):
205191
if err < 0:
206192
raise BridgeException(-err)
207193

208-
209-
class ConnInfoReadError(Exception):
210-
pass
211-
212-
cdef class conn_info:
213-
cdef conn_info_handles ci
214-
cdef int hci
215-
cdef char* addr
216-
cdef public bint failed
217-
218-
def __init__(self, py_addr, py_hci_name="hci0"):
219-
self.failed = False
220-
py_bytes_addr = py_addr.encode("UTF-8")
221-
cdef char* addr = py_bytes_addr
222-
223-
py_bytes_hci_name = py_hci_name.encode("UTF-8")
224-
cdef char* hci_name = py_bytes_hci_name
225-
226-
227-
self.hci = int(hci_name[3:])
228-
self.addr = addr
229-
230-
def init(self):
231-
res = connection_init(self.hci, self.addr, & self.ci)
232-
if res < 0:
233-
self.failed = True
234-
raise ConnInfoReadError(ERR[res])
235-
236-
def deinit(self):
237-
if self.failed:
238-
return
239-
connection_close(&self.ci)
240-
241-
def get_rssi(self):
242-
cdef signed char rssi
243-
res = connection_get_rssi(&self.ci, &rssi)
244-
if res < 0:
245-
raise ConnInfoReadError(ERR[res])
246-
247-
return rssi
248-
249-
def get_tpl(self, tp=0):
250-
cdef signed char tpl
251-
res = connection_get_tpl(&self.ci, &tpl, tp)
252-
if res < 0:
253-
raise ConnInfoReadError(ERR[res])
254-
255-
return tpl
256-
257194
def device_info(py_hci_name="hci0"):
258195
py_bytes_hci_name = py_hci_name.encode("UTF-8")
259196
cdef char* hci_name = py_bytes_hci_name

module/libblueman.c

-105
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
#include <linux/if_bridge.h>
3636

3737
#include <bluetooth/bluetooth.h>
38-
#include <bluetooth/hci.h>
39-
#include <bluetooth/hci_lib.h>
4038
#include <bluetooth/rfcomm.h>
4139
#include <bluetooth/sdp.h>
4240
#include <bluetooth/sdp_lib.h>
@@ -132,109 +130,6 @@ int _destroy_bridge(const char* name) {
132130
return 0;
133131
}
134132

135-
static int find_conn(int s, int dev_id, long arg)
136-
{
137-
struct hci_conn_list_req *cl;
138-
struct hci_conn_info *ci;
139-
int i;
140-
int ret = 0;
141-
142-
if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl))))
143-
goto out;
144-
145-
cl->dev_id = dev_id;
146-
cl->conn_num = 10;
147-
ci = cl->conn_info;
148-
149-
if (ioctl(s, HCIGETCONNLIST, (void *) cl))
150-
goto out;
151-
152-
for (i = 0; i < cl->conn_num; i++, ci++)
153-
if (!bacmp((bdaddr_t *) arg, &ci->bdaddr)) {
154-
ret = 1;
155-
goto out;
156-
}
157-
158-
out:
159-
free(cl);
160-
return ret;
161-
}
162-
163-
164-
165-
int connection_init(int dev_id, char *addr, struct conn_info_handles *ci)
166-
{
167-
struct hci_conn_info_req *cr = NULL;
168-
bdaddr_t bdaddr;
169-
170-
int dd;
171-
int ret = 1;
172-
173-
str2ba(addr, &bdaddr);
174-
175-
if (dev_id < 0) {
176-
dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
177-
if (dev_id < 0) {
178-
ret = ERR_NOT_CONNECTED;
179-
goto out;
180-
}
181-
}
182-
183-
dd = hci_open_dev(dev_id);
184-
if (dd < 0) {
185-
ret = ERR_HCI_DEV_OPEN_FAILED;
186-
goto out;
187-
}
188-
189-
cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
190-
if (!cr) {
191-
ret = ERR_CANNOT_ALLOCATE;
192-
goto out;
193-
}
194-
195-
bacpy(&cr->bdaddr, &bdaddr);
196-
cr->type = ACL_LINK;
197-
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
198-
ret = ERR_GET_CONN_INFO_FAILED;
199-
goto out;
200-
}
201-
202-
ci->dd = dd;
203-
ci->handle = cr->conn_info->handle;
204-
205-
out:
206-
if (cr)
207-
free(cr);
208-
209-
return ret;
210-
}
211-
212-
int connection_get_rssi(struct conn_info_handles *ci, int8_t *ret_rssi)
213-
{
214-
int8_t rssi;
215-
if (hci_read_rssi(ci->dd, htobs(ci->handle), &rssi, 1000) < 0) {
216-
return ERR_READ_RSSI_FAILED;
217-
}
218-
*ret_rssi = rssi;
219-
return 1;
220-
221-
}
222-
223-
int connection_get_tpl(struct conn_info_handles *ci, int8_t *ret_tpl, uint8_t type)
224-
{
225-
int8_t level;
226-
if (hci_read_transmit_power_level(ci->dd, htobs(ci->handle), type, &level, 1000) < 0) {
227-
return ERR_READ_TPL_FAILED;
228-
}
229-
*ret_tpl = level;
230-
return 1;
231-
}
232-
233-
int connection_close(struct conn_info_handles *ci)
234-
{
235-
hci_close_dev(ci->dd);
236-
return 1;
237-
}
238133

239134
int
240135
get_rfcomm_channel(uint16_t service_class, char* btd_addr) {

module/libblueman.h

-14
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,12 @@
11
#pragma once
22
#define ERR_CANNOT_ALLOCATE -1
3-
#define ERR_HCI_DEV_OPEN_FAILED -2
4-
#define ERR_NOT_CONNECTED -3
5-
#define ERR_GET_CONN_INFO_FAILED -4
6-
#define ERR_READ_RSSI_FAILED -5
7-
#define ERR_READ_TPL_FAILED -6
83
#define ERR_GET_RFCOMM_LIST_FAILED -8
94
#define ERR_SOCKET_FAILED -9
105
#define ERR_BIND_FAILED -12
116
#define ERR_CONNECT_FAILED -13
127
#define ERR_CREATE_DEV_FAILED -14
138
#define ERR_RELEASE_DEV_FAILED -15
149

15-
struct conn_info_handles {
16-
unsigned int handle;
17-
int dd;
18-
};
19-
20-
int connection_init(int dev_id, char *addr, struct conn_info_handles *ci);
21-
int connection_get_rssi(struct conn_info_handles *ci, int8_t *ret_rssi);
22-
int connection_get_tpl(struct conn_info_handles *ci, int8_t *ret_tpl, uint8_t type);
23-
int connection_close(struct conn_info_handles *ci);
2410
int get_rfcomm_channel(uint16_t uuid, char* btd_addr);
2511
int get_rfcomm_list(struct rfcomm_dev_list_req **result);
2612
int create_rfcomm_device(char *local_address, char *remote_address, int channel);

stubs/_blueman.pyi

-10
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,8 @@ class BridgeException(Exception):
3535
errno: int
3636
def __init__(self, errno: int) -> None: ...
3737

38-
class ConnInfoReadError(Exception): ...
39-
4038
class RFCOMMError(Exception): ...
4139

42-
class conn_info:
43-
failed: bool
44-
def __init__(self, addr: str, hci_name: str) -> None: ...
45-
def deinit(self) -> None: ...
46-
def get_rssi(self) -> int: ...
47-
def get_tpl(self) -> int: ...
48-
def init(self) -> None: ...
49-
5040
def create_bridge(name: str = "pan1") -> None: ...
5141
def create_rfcomm_device(local_address: str, remote_address: str, channel: int) -> int: ...
5242
def destroy_bridge(name: str = "pan1") -> None: ...

0 commit comments

Comments
 (0)