#include "server.hh" #include "bt.hh" #include "cfg.hh" #include "config.h" #include "http.hh" #include "json.hh" #include "logger.hh" #include "looper.hh" #include "signals.hh" #include "uri.hh" #include "websocket.hh" #include #include #include #include #include #include #include namespace server { namespace { const std::string_view kSignalUpdateAdapter("controller/update"); const std::string kSignalUpdateDevicePrefix("device/update/"); std::optional 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) : api_(api), signaler_(signaler), json_writer_(json::writer(json_tmp_)), json_mimetype_(http::MimeType::create("application", "json")) {} std::unique_ptr handle( http::Request const& request) override { if (request.path().starts_with("/api/v1/")) { auto path = request.path().substr(8); if (path == "status") { 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 const* adapter = api_.adapter(); json_writer_->clear(); write_adapter(adapter); 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 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.starts_with("device/")) { if (request.method() != "GET") { return http::Response::status(http::StatusCode::kMethodNotAllowed); } std::string tmp; auto address = uri::decode(path.substr(7), tmp); auto const* adapter = api_.adapter(); if (adapter) { for (auto* device : adapter->devices()) { if (device->address() == address) { json_writer_->clear(); write_device(*device); return http::Response::content(json_tmp_, *json_mimetype_); } } } return http::Response::status(http::StatusCode::kNotFound); } if (path == "events") { if (request.method() != "GET") { return http::Response::status(http::StatusCode::kMethodNotAllowed); } auto resp = signaler_.handle(request); if (resp) return resp; return http::Response::status(http::StatusCode::kBadRequest); } } return http::Response::status(http::StatusCode::kNotFound); } private: std::unique_ptr 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 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_); } void write_adapter(bt::Adapter const* adapter) { json_writer_->start_object(); json_writer_->key("name"); 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_->key("devices"); json_writer_->start_array(); if (adapter) { for (auto* device : adapter->devices()) { write_device(*device); } } json_writer_->end_array(); json_writer_->end_object(); } void write_device(bt::Device const& device) { json_writer_->start_object(); json_writer_->key("name"); json_writer_->value(device.name()); json_writer_->key("address"); json_writer_->value(device.address()); json_writer_->key("paired"); json_writer_->value(device.paired()); json_writer_->key("connected"); json_writer_->value(device.connected()); json_writer_->key("playing"); if (auto* player = device.player()) { json_writer_->start_object(); json_writer_->key("status"); switch (player->status()) { case bt::Player::Status::kPlaying: case bt::Player::Status::kForwardSeek: case bt::Player::Status::kReverseSeek: json_writer_->value("playing"); break; case bt::Player::Status::kStopped: json_writer_->value("stopped"); break; case bt::Player::Status::kPaused: json_writer_->value("paused"); break; case bt::Player::Status::kError: json_writer_->value("error"); break; } json_writer_->key("title"); json_writer_->value(player->track_title()); json_writer_->key("album"); json_writer_->value(player->track_album()); json_writer_->key("artist"); json_writer_->value(player->track_artist()); json_writer_->end_object(); } else { json_writer_->value(nullptr); } json_writer_->end_object(); } Api& api_; Signaler& signaler_; std::unique_ptr json_writer_; std::string json_tmp_; std::unique_ptr json_mimetype_; }; class BluetoothManagerDelegate : public Api { public: BluetoothManagerDelegate(logger::Logger& logger, Signaler& signaler) : logger_(logger), signaler_(signaler) {} [[nodiscard]] bt::Adapter* adapter() const override { return adapter_; } void new_adapter(bt::Adapter* adapter) override { adapter_ = adapter; signaler_.send(kSignalUpdateAdapter); 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"); } } void updated_adapter(bt::Adapter& adapter) override { if (adapter_ == &adapter) signaler_.send(kSignalUpdateAdapter); } void added_device(bt::Device& device) override { logger_.info( std::format("New device: {} [{}]", device.name(), device.address())); if (adapter_) signaler_.send(kSignalUpdateAdapter); } void removed_device(std::string const& address) override { logger_.info(std::format("Remove device: [{}]", address)); if (adapter_) signaler_.send(kSignalUpdateAdapter); } void added_player(bt::Device& device, bt::Player& /* player */) override { logger_.info(std::format("New player for {}", device.name())); if (adapter_) signaler_.send(kSignalUpdateDevicePrefix + device.address()); } void removed_player(bt::Device& device) override { logger_.info(std::format("Remove player for {}", device.name())); if (adapter_) signaler_.send(kSignalUpdateDevicePrefix + device.address()); } void updated_player(bt::Device& device, bt::Player& /* player */) override { if (adapter_) signaler_.send(kSignalUpdateDevicePrefix + device.address()); } void agent_request_pincode( bt::Device& device, std::function)> callback) override { logger_.dbg(std::format("Device request pincode: {}", device.name())); callback(std::nullopt); } void agent_display_pincode(bt::Device& device, std::string const& pincode) override { logger_.dbg(std::format("Device pincode: {} {}", device.name(), pincode)); } void agent_request_passkey( bt::Device& device, std::function)> callback) override { logger_.dbg(std::format("Device request passkey: {}", device.name())); callback(std::nullopt); } void agent_display_passkey(bt::Device& device, uint32_t passkey, uint16_t /* entered */) override { logger_.dbg(std::format("Device passkey: {} {}", device.name(), passkey)); } void agent_request_confirmation(bt::Device& device, uint32_t passkey, std::function callback) override { logger_.dbg(std::format("Device request confirmation: {} {}", device.name(), passkey)); // Confirm all callback(true); } void agent_request_authorization( bt::Device& device, std::function callback) override { logger_.dbg(std::format("Device request authorization: {}", device.name())); callback(false); } void agent_authorize_service(bt::Device& device, std::string const& uuid, std::function callback) override { if (uuid == "0000110d-0000-1000-8000-00805f9b34fb") { // Advanced Audio Distribution Profile callback(true); return; } if (uuid == "0000111e-0000-1000-8000-00805f9b34fb") { // Hands-Free Service Class and Profile // Not interrested. callback(false); return; } logger_.dbg(std::format("Device request authorize unknown service: {} {}", device.name(), uuid)); // Do not authorize unknown services. callback(false); } void agent_cancel() override {} private: logger::Logger& logger_; Signaler& signaler_; bt::Adapter* adapter_{nullptr}; }; class SignalerImpl : public Signaler, ws::Server::Delegate { public: SignalerImpl(logger::Logger& logger, cfg::Config const& cfg, looper::Looper& looper) : server_(ws::create_server(logger, cfg, looper, *this)) {} void send(std::string_view signal) override { server_->send_text_to_all(signal); } std::unique_ptr handle( http::Request const& request) override { return server_->handle(request); } std::unique_ptr handle(ws::Message const& /* msg */) override { // Ignore anything sent by clients return nullptr; } private: std::unique_ptr server_; }; } // namespace std::unique_ptr create_signaler(logger::Logger& logger, cfg::Config const& cfg, looper::Looper& looper) { return std::make_unique(logger, cfg, looper); } std::unique_ptr create_bt_delegate(logger::Logger& logger, Signaler& signaler) { return std::make_unique(logger, signaler); } std::unique_ptr create_http_delegate( Api& api, Signaler& signaler) { return std::make_unique(api, signaler); } } // namespace server