From e7c74917191a4953d495295b65732aa3549ba753 Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Sun, 19 Oct 2025 00:12:50 +0200 Subject: Add new module websocket and use it Implement /api/v1/events which will send out messages when things change, such as the main controller. --- src/main.cc | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 9 deletions(-) (limited to 'src/main.cc') diff --git a/src/main.cc b/src/main.cc index 6883f30..494c49c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -3,9 +3,11 @@ #include "cfg.hh" #include "config.h" #include "http.hh" +#include "json.hh" #include "logger.hh" #include "looper.hh" #include "signals.hh" +#include "websocket.hh" #include #include @@ -16,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -26,9 +29,38 @@ namespace { +class Api { + public: + virtual ~Api() = default; + + [[nodiscard]] + virtual bt::Adapter* adapter() const = 0; + + protected: + Api() = default; +}; + +const std::string_view kSignalUpdateAdapter("controller/update"); + +class Signaler { + public: + virtual ~Signaler() = default; + + virtual void send(std::string_view signal) = 0; + virtual std::unique_ptr handle( + http::Request const& request) = 0; + + protected: + Signaler() = default; +}; + class HttpServerDelegate : public http::Server::Delegate { public: - HttpServerDelegate() = default; + 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 { @@ -36,21 +68,66 @@ class HttpServerDelegate : public http::Server::Delegate { return http::Response::status(http::StatusCode::kMethodNotAllowed); } - if (request.path() == "/api/v1/status") { - return http::Response::content( - R"({ status: "OK" })", - *http::MimeType::create("application", "json")); + 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 (path == "controller") { + auto* adapter = api_.adapter(); + json_writer_->clear(); + 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("pairing"); + json_writer_->value(adapter ? adapter->pairing() : false); + json_writer_->end_object(); + + return http::Response::content(json_tmp_, *json_mimetype_); + } + + if (path == "events") { + auto resp = signaler_.handle(request); + if (resp) + return resp; + return http::Response::status(http::StatusCode::kBadRequest); + } } return http::Response::status(http::StatusCode::kNotFound); } + + private: + Api& api_; + Signaler& signaler_; + std::unique_ptr json_writer_; + std::string json_tmp_; + std::unique_ptr json_mimetype_; }; -class BluetoothManagerDelegate : public bt::Manager::Delegate { +class BluetoothManagerDelegate : public bt::Manager::Delegate, public Api { public: - explicit BluetoothManagerDelegate(logger::Logger& logger) : logger_(logger) {} + 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())); @@ -59,6 +136,11 @@ class BluetoothManagerDelegate : public bt::Manager::Delegate { } } + 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())); @@ -117,15 +199,42 @@ class BluetoothManagerDelegate : public bt::Manager::Delegate { 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_; }; bool run(logger::Logger& logger, cfg::Config const& cfg, std::unique_ptr port) { auto looper = looper::create(); - HttpServerDelegate http_delegate; + SignalerImpl signaler(logger, cfg, *looper); + BluetoothManagerDelegate bt_delegate(logger, signaler); + HttpServerDelegate http_delegate(bt_delegate, signaler); auto server = http::create_server(logger, cfg, *looper, std::move(port), http_delegate); - BluetoothManagerDelegate bt_delegate(logger); auto manager = bt::create_manager(logger, cfg, *looper, bt_delegate); auto sigint_handler = signals::Handler::create( *looper, signals::Signal::INT, [&looper, &logger]() { -- cgit v1.2.3-70-g09d2