summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bt.cc87
-rw-r--r--src/bt.hh16
-rw-r--r--src/main.cc79
3 files changed, 168 insertions, 14 deletions
diff --git a/src/bt.cc b/src/bt.cc
index e3c12d9..e59555d 100644
--- a/src/bt.cc
+++ b/src/bt.cc
@@ -368,6 +368,10 @@ class AdapterProxy : public BaseProxy, public Adapter {
{sdbus::PropertyName{"Discoverable"}, 3},
{sdbus::PropertyName{"Pairable"}, 4},
{sdbus::PropertyName{"Pairing"}, 5},
+ {sdbus::PropertyName{"Connectable"}, 6},
+ {sdbus::PropertyName{"Powered"}, 7},
+ {sdbus::PropertyName{"DiscoverableTimeout"}, 8},
+ {sdbus::PropertyName{"PairableTimeout"}, 9},
};
public:
@@ -399,20 +403,71 @@ class AdapterProxy : public BaseProxy, public Adapter {
}
[[nodiscard]]
+ uint32_t discoverable_timeout_seconds() const override {
+ return discoverable_timeout_;
+ }
+
+ [[nodiscard]]
bool pairable() const override {
return pairable_;
}
[[nodiscard]]
+ uint32_t pairable_timeout_seconds() const override {
+ return pairable_timeout_;
+ }
+
+ [[nodiscard]]
bool pairing() const override {
return pairing_;
}
[[nodiscard]]
+ bool powered() const override {
+ return powered_;
+ }
+
+ [[nodiscard]]
+ bool connectable() const override {
+ return connectable_;
+ }
+
+ [[nodiscard]]
std::vector<Device*> devices() const override {
return manager_.get_devices(getProxy().getObjectPath());
}
+ void set_discoverable(bool discoverable) override {
+ if (discoverable == discoverable_)
+ return;
+
+ SetAsync(kAdapterInterface, sdbus::PropertyName{"Discoverable"},
+ sdbus::Variant{discoverable},
+ [this](std::optional<sdbus::Error> err) {
+ set_callback(std::move(err));
+ });
+ }
+
+ void set_discoverable_timeout_seconds(uint32_t seconds) override {
+ if (discoverable_timeout_ == seconds)
+ return;
+
+ SetAsync(kAdapterInterface, sdbus::PropertyName{"DiscoverableTimeout"},
+ sdbus::Variant{seconds}, [this](std::optional<sdbus::Error> err) {
+ set_callback(std::move(err));
+ });
+ }
+
+ void set_pairable_timeout_seconds(uint32_t seconds) override {
+ if (pairable_timeout_ == seconds)
+ return;
+
+ SetAsync(kAdapterInterface, sdbus::PropertyName{"PairableTimeout"},
+ sdbus::Variant{seconds}, [this](std::optional<sdbus::Error> err) {
+ set_callback(std::move(err));
+ });
+ }
+
private:
void update(size_t field, sdbus::Variant const& value) override {
switch (field) {
@@ -430,16 +485,35 @@ class AdapterProxy : public BaseProxy, public Adapter {
break;
case 4:
pairable_ = value.get<bool>();
- manager_.logger().dbg(std::format("pairable: {}", pairable_));
break;
case 5:
pairing_ = value.get<bool>();
break;
+ case 6:
+ connectable_ = value.get<bool>();
+ break;
+ case 7:
+ powered_ = value.get<bool>();
+ break;
+ case 8:
+ discoverable_timeout_ = value.get<uint32_t>();
+ break;
+ case 9:
+ pairable_timeout_ = value.get<uint32_t>();
+ break;
default:
std::unreachable();
}
}
+ void set_callback(std::optional<sdbus::Error> err) {
+ if (err.has_value()) {
+ manager_.logger().warn(std::format("Failed to set property: {} {}",
+ std::string(err->getName()),
+ err->getMessage()));
+ }
+ }
+
void notify_updated() override {
manager_.update_adapter(getProxy().getObjectPath());
}
@@ -451,6 +525,10 @@ class AdapterProxy : public BaseProxy, public Adapter {
bool discoverable_{false};
bool pairable_{false};
bool pairing_{false};
+ bool connectable_{false};
+ bool powered_{false};
+ uint32_t discoverable_timeout_{180};
+ uint32_t pairable_timeout_{0};
};
class AgentManagerProxy {
@@ -914,16 +992,17 @@ class ManagerImpl : public Manager, public IManager, looper::Hook {
auto cfg_adapter = cfg_.get("bluetooth.adapter");
if (cfg_adapter.has_value()) {
for (auto const& pair : adapters_) {
- if (pair.second->address() == cfg_adapter.value()) {
+ if (pair.second->address() == cfg_adapter.value() &&
+ pair.second->powered()) {
primary_adapter_ = pair.first;
delegate_.new_adapter(pair.second.get());
return;
}
}
} else {
- // Pick first (map is sorted) that is pairable.
+ // Pick first (map is sorted) that is pairable and powered.
for (auto const& pair : adapters_) {
- if (pair.second->pairable()) {
+ if (pair.second->powered() && pair.second->pairable()) {
primary_adapter_ = pair.first;
delegate_.new_adapter(pair.second.get());
return;
diff --git a/src/bt.hh b/src/bt.hh
index 53d3919..588a328 100644
--- a/src/bt.hh
+++ b/src/bt.hh
@@ -38,14 +38,30 @@ class Adapter {
virtual bool discoverable() const = 0;
[[nodiscard]]
+ virtual uint32_t discoverable_timeout_seconds() const = 0;
+
+ [[nodiscard]]
virtual bool pairable() const = 0;
[[nodiscard]]
+ virtual uint32_t pairable_timeout_seconds() const = 0;
+
+ [[nodiscard]]
virtual bool pairing() const = 0;
[[nodiscard]]
+ virtual bool powered() const = 0;
+
+ [[nodiscard]]
+ virtual bool connectable() const = 0;
+
+ [[nodiscard]]
virtual std::vector<Device*> devices() const = 0;
+ virtual void set_discoverable(bool discoverable) = 0;
+ virtual void set_discoverable_timeout_seconds(uint32_t timeout) = 0;
+ virtual void set_pairable_timeout_seconds(uint32_t timeout) = 0;
+
protected:
Adapter() = default;
Adapter(Adapter const&) = delete;
diff --git a/src/main.cc b/src/main.cc
index 494c49c..4555473 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -54,6 +54,14 @@ class Signaler {
Signaler() = default;
};
+std::optional<bool> match_bool(std::string_view str) {
+ if (str == "true")
+ return true;
+ if (str == "false")
+ return false;
+ return std::nullopt;
+}
+
class HttpServerDelegate : public http::Server::Delegate {
public:
HttpServerDelegate(Api& api, Signaler& signaler)
@@ -64,22 +72,21 @@ class HttpServerDelegate : public http::Server::Delegate {
std::unique_ptr<http::Response> handle(
http::Request const& request) override {
- if (request.method() != "GET") {
- return http::Response::status(http::StatusCode::kMethodNotAllowed);
- }
-
if (request.path().starts_with("/api/v1/")) {
auto path = request.path().substr(8);
if (path == "status") {
- json_writer_->clear();
- json_writer_->start_object();
- json_writer_->key("status");
- json_writer_->value("OK");
- json_writer_->end_object();
- return http::Response::content(json_tmp_, *json_mimetype_);
+ if (request.method() != "GET") {
+ return http::Response::status(http::StatusCode::kMethodNotAllowed);
+ }
+
+ return status_ok();
}
if (path == "controller") {
+ if (request.method() != "GET") {
+ return http::Response::status(http::StatusCode::kMethodNotAllowed);
+ }
+
auto* adapter = api_.adapter();
json_writer_->clear();
json_writer_->start_object();
@@ -87,6 +94,11 @@ class HttpServerDelegate : public http::Server::Delegate {
json_writer_->value(adapter ? adapter->name() : "unknown");
json_writer_->key("pairable");
json_writer_->value(adapter ? adapter->pairable() : false);
+ json_writer_->key("discoverable");
+ json_writer_->value(adapter ? adapter->discoverable() : false);
+ json_writer_->key("discoverable_timeout_seconds");
+ json_writer_->value(adapter ? adapter->discoverable_timeout_seconds()
+ : 0);
json_writer_->key("pairing");
json_writer_->value(adapter ? adapter->pairing() : false);
json_writer_->end_object();
@@ -94,7 +106,24 @@ class HttpServerDelegate : public http::Server::Delegate {
return http::Response::content(json_tmp_, *json_mimetype_);
}
+ if (path == "controller/discoverable") {
+ if (request.method() != "POST") {
+ return http::Response::status(http::StatusCode::kMethodNotAllowed);
+ }
+ std::optional<bool> value = match_bool(request.body());
+ auto* adapter = api_.adapter();
+ if (adapter && value.has_value()) {
+ adapter->set_discoverable(value.value());
+ return status_ok();
+ }
+ return status_error("Bad state");
+ }
+
if (path == "events") {
+ if (request.method() != "GET") {
+ return http::Response::status(http::StatusCode::kMethodNotAllowed);
+ }
+
auto resp = signaler_.handle(request);
if (resp)
return resp;
@@ -106,6 +135,26 @@ class HttpServerDelegate : public http::Server::Delegate {
}
private:
+ std::unique_ptr<http::Response> status_ok() {
+ json_writer_->clear();
+ json_writer_->start_object();
+ json_writer_->key("status");
+ json_writer_->value("OK");
+ json_writer_->end_object();
+ return http::Response::content(json_tmp_, *json_mimetype_);
+ }
+
+ std::unique_ptr<http::Response> status_error(std::string_view message) {
+ json_writer_->clear();
+ json_writer_->start_object();
+ json_writer_->key("status");
+ json_writer_->value("error");
+ json_writer_->key("message");
+ json_writer_->value(message);
+ json_writer_->end_object();
+ return http::Response::content(json_tmp_, *json_mimetype_);
+ }
+
Api& api_;
Signaler& signaler_;
std::unique_ptr<json::Writer> json_writer_;
@@ -131,6 +180,16 @@ class BluetoothManagerDelegate : public bt::Manager::Delegate, public Api {
if (adapter) {
logger_.info(std::format("New adapter: {} [{}]", adapter->name(),
adapter->address()));
+
+ // Assuming pairable doesn't have a timeout - make it so.
+ if (adapter_->pairable_timeout_seconds() > 0) {
+ adapter_->set_pairable_timeout_seconds(0);
+ }
+
+ // Assuming discoverable has one
+ if (adapter_->discoverable_timeout_seconds() == 0) {
+ adapter_->set_discoverable_timeout_seconds(180);
+ }
} else {
logger_.info("No adapter");
}