30 static bool v =
false;
37 snprintf(buf,
sizeof buf,
"0000%04x-0000-1000-8000-00805f9b34fb", uuid16);
42 static GDBusConnection*
bus(std::string& error) {
43 static GDBusConnection* conn =
nullptr;
44 if (conn ==
nullptr) {
45 GError* err =
nullptr;
46 conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM,
nullptr, &err);
47 if (conn ==
nullptr) {
48 error = std::string(
"cannot connect to the system bus: ") + err->message;
56 static GVariant*
call(
const std::string& path,
const char* iface,
const char* method,
57 GVariant* params, std::string& error,
int timeoutMs = 20000) {
58 GDBusConnection* conn =
bus(error);
59 if (conn ==
nullptr) {
60 if (params !=
nullptr && g_variant_is_floating(params)) g_variant_unref(params);
63 GError* err =
nullptr;
64 GVariant* result = g_dbus_connection_call_sync(
65 conn,
"org.bluez", path.c_str(), iface, method, params,
nullptr,
66 G_DBUS_CALL_FLAGS_NONE, timeoutMs,
nullptr, &err);
67 if (result ==
nullptr) {
68 g_dbus_error_strip_remote_error(err);
70 if (
verbose()) fprintf(stderr,
"%s.%s failed: %s\n", iface, method, err->message);
73 fprintf(stderr,
"%s.%s ok\n", iface, method);
80 std::string path, address, name;
84 bool connected =
false;
89 static std::string
findAdapter(
const std::string& name, std::string& error) {
90 GVariant* objs =
call(
"/",
"org.freedesktop.DBus.ObjectManager",
"GetManagedObjects",
92 if (objs ==
nullptr) {
93 error =
"cannot talk to BlueZ: " + error +
" (is the bluetooth service running?)";
96 std::string wanted =
"/org/bluez/" + name;
99 g_variant_get(objs,
"(a{oa{sa{sv}}})", &it);
102 while (g_variant_iter_next(it,
"{&o@a{sa{sv}}}", &path, &ifaces)) {
103 GVariant* a = g_variant_lookup_value(ifaces,
"org.bluez.Adapter1",
nullptr);
105 if (wanted == path) found = path;
108 g_variant_unref(ifaces);
110 g_variant_iter_free(it);
111 g_variant_unref(objs);
112 if (found.empty()) error =
"no Bluetooth adapter " + name +
" found";
116 static std::vector<Device> listDevices(
const std::string& adapter) {
117 std::vector<Device> devices;
119 GVariant* objs =
call(
"/",
"org.freedesktop.DBus.ObjectManager",
"GetManagedObjects",
121 if (objs ==
nullptr)
return devices;
124 g_variant_get(objs,
"(a{oa{sa{sv}}})", &it);
127 while (g_variant_iter_next(it,
"{&o@a{sa{sv}}}", &path, &ifaces)) {
128 GVariant* p = g_variant_lookup_value(ifaces,
"org.bluez.Device1", G_VARIANT_TYPE(
"a{sv}"));
129 if (p !=
nullptr && g_str_has_prefix(path, (adapter +
"/").c_str())) {
133 if (g_variant_lookup(p,
"Address",
"&s", &s)) d.address = s;
134 if (g_variant_lookup(p,
"Alias",
"&s", &s)) d.name = s;
135 if (g_variant_lookup(p,
"Name",
"&s", &s)) d.name = s;
137 if (g_variant_lookup(p,
"RSSI",
"n", &rssi)) {
142 if (g_variant_lookup(p,
"ServicesResolved",
"b", &b)) d.resolved = b;
143 if (g_variant_lookup(p,
"Connected",
"b", &b)) d.connected = b;
144 GVariant* uuids = g_variant_lookup_value(p,
"UUIDs", G_VARIANT_TYPE(
"as"));
145 if (uuids !=
nullptr) {
147 const gchar** list = g_variant_get_strv(uuids, &n);
148 for (gsize i = 0; i < n; i++) {
149 if (cts == list[i]) d.cts =
true;
152 g_variant_unref(uuids);
154 devices.push_back(d);
156 if (p !=
nullptr) g_variant_unref(p);
157 g_variant_unref(ifaces);
159 g_variant_iter_free(it);
160 g_variant_unref(objs);
166 std::string result, error;
167 GVariant* objs =
call(
"/",
"org.freedesktop.DBus.ObjectManager",
"GetManagedObjects",
169 if (objs ==
nullptr)
return result;
170 std::string uuid =
fullUuid(uuid16);
172 g_variant_get(objs,
"(a{oa{sa{sv}}})", &it);
175 while (g_variant_iter_next(it,
"{&o@a{sa{sv}}}", &path, &ifaces)) {
176 GVariant* p = g_variant_lookup_value(ifaces,
"org.bluez.GattCharacteristic1",
177 G_VARIANT_TYPE(
"a{sv}"));
180 if (g_str_has_prefix(path, (devicePath +
"/").c_str()) &&
181 g_variant_lookup(p,
"UUID",
"&s", &s) && uuid == s) {
186 g_variant_unref(ifaces);
188 g_variant_iter_free(it);
189 g_variant_unref(objs);
193 static bool readValue(
const std::string& characteristic, std::vector<uint8_t>& out,
194 std::string& error) {
195 GVariant* r =
call(characteristic,
"org.bluez.GattCharacteristic1",
"ReadValue",
196 g_variant_new(
"(a{sv})",
nullptr), error);
197 if (r ==
nullptr)
return false;
198 GVariant* bytes = g_variant_get_child_value(r, 0);
200 const uint8_t* data =
static_cast<const uint8_t*
>(g_variant_get_fixed_array(bytes, &n, 1));
201 out.assign(data, data + n);
202 g_variant_unref(bytes);
207 static bool writeValue(
const std::string& characteristic,
const uint8_t* data,
size_t len,
208 std::string& error) {
209 GVariant* bytes = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, data, len, 1);
210 GVariant* r =
call(characteristic,
"org.bluez.GattCharacteristic1",
"WriteValue",
211 g_variant_new(
"(@aya{sv})", bytes,
nullptr), error);
212 if (r ==
nullptr)
return false;
217 static bool equalsIgnoreCase(
const std::string& a,
const std::string& b) {
218 return a.size() == b.size() &&
219 std::equal(a.begin(), a.end(), b.begin(),
220 [](
char x,
char y) { return tolower(x) == tolower(y); });
224 template <
typename Found>
225 static std::vector<Device>
discover(
const std::string& adapter, uint32_t timeoutMs,
226 Found found, std::string& error) {
227 call(adapter,
"org.bluez.Adapter1",
"SetDiscoveryFilter",
228 g_variant_new_parsed(
"({'Transport': <'le'>, 'DuplicateData': <false>},)"), error);
230 call(adapter,
"org.bluez.Adapter1",
"StartDiscovery",
nullptr, error) !=
nullptr;
231 if (!started && error.find(
"InProgress") != std::string::npos) error.clear();
232 std::vector<Device> devices;
233 auto t0 = std::chrono::steady_clock::now();
235 devices = listDevices(adapter);
236 if (found(devices))
break;
237 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
238 std::chrono::steady_clock::now() - t0)
240 if (ms >= timeoutMs)
break;
241 g_usleep(250 * 1000);
244 if (started)
call(adapter,
"org.bluez.Adapter1",
"StopDiscovery",
nullptr, ignored);
The data of the Bluetooth Current Time Service (CTS, 0x1805): UUIDs and the encoding of the character...
constexpr uint16_t CTS_SERVICE_UUID
16 bit UUID of the Current Time Service
Definition TinyCTSTypes.h:17
A device that BlueZ knows.
Definition TinyCTSBlueZ.h:79
bool cts
the UUIDs include the Current Time Service
Definition TinyCTSBlueZ.h:81
bool resolved
ServicesResolved.
Definition TinyCTSBlueZ.h:83
bool seen
RSSI known: in range during the discovery.
Definition TinyCTSBlueZ.h:82
The functions to talk to BlueZ (all static); the BlueZ client and server use them.
Definition TinyCTSBlueZ.h:26
static std::string findAdapter(const std::string &name, std::string &error)
The object path of the Bluetooth adapter (empty and the message in error if not found).
Definition TinyCTSBlueZ.h:89
static std::vector< Device > discover(const std::string &adapter, uint32_t timeoutMs, Found found, std::string &error)
Look for devices. The scan stops when found(devices) returns true, or at the timeout.
Definition TinyCTSBlueZ.h:225
static GDBusConnection * bus(std::string &error)
The connection to the system bus (nullptr and the message in error if not possible).
Definition TinyCTSBlueZ.h:42
static std::string fullUuid(uint16_t uuid16)
The 128 bit UUID of a 16 bit UUID.
Definition TinyCTSBlueZ.h:35
static bool & verbose()
Print the D-Bus calls to stderr.
Definition TinyCTSBlueZ.h:29
static std::string findCharacteristic(const std::string &devicePath, uint16_t uuid16)
The object path of a characteristic of a connected device (empty if not found).
Definition TinyCTSBlueZ.h:165
static GVariant * call(const std::string &path, const char *iface, const char *method, GVariant *params, std::string &error, int timeoutMs=20000)
A synchronous call to BlueZ. Returns nullptr and the message in error.
Definition TinyCTSBlueZ.h:56