Files
cuframes-composer/include/cuframes_composer/cpp/mqtt_overlay.hpp
T
gx 75271436f7 Phase 11b: MQTT-overlays конфигурируются через JSON (вместо --temp-topic)
User: "оверлеев которые выводят какую-то инфу из MQTT может быть
бесконечно много. Надо делать настраиваемым через json/yaml конфиг".

Заменили захардкоженный TempMqttOverlay на generic MqttOverlay:

  - cpp/mqtt_overlay.hpp/.cpp: класс MqttOverlayItem (один topic + один
    text overlay) + MqttOverlayManager (контейнер). Загрузка из JSON.
  - cpp/mqtt_overlay_c_api.cpp: extern "C" обёртка для grid_record.c.
  - docker/mqtt_overlays.json: default config с temp_outside примером.
  - grid_record.c: --mqtt-overlays=PATH (заменил --temp-topic).
  - src/CMakeLists.txt: temp_overlay* удалены, mqtt_overlay* добавлены.

JSON schema:
  {
    "overlays": [
      {
        "id": "temp_outside",
        "topic": "zigbee2mqtt/Температура на улице",
        "json_field": "temperature",      // пусто = raw payload string
        "format": "%+.1f°C",                // printf
        "anchor": "right-bottom",           // right-top, left-bottom, ...
        "margin_x": 32, "margin_y": 24,
        "pixel_size": 32,
        "color": [255, 255, 255], "alpha": 230,
        "font_path": "/fonts/DejaVuSans-Bold.ttf"
      }
    ]
  }

MqttBrokerCfg делятся между всеми overlays (one connect_async per item
но shared credentials). Добавление новых overlays = редактирование JSON +
restart cfc-grid (hot-reload через ZMQ — Phase 12).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 10:07:05 +01:00

114 lines
3.7 KiB
C++

/* MqttOverlay — generic MQTT-driven text overlay (Phase 11b).
*
* Каждый overlay = одна MQTT-подписка + один persistent text overlay.
* Конфиг загружается из JSON-файла (mqtt_overlays.json):
* {
* "overlays": [
* { "id": "temp_outside",
* "topic": "zigbee2mqtt/Температура на улице",
* "json_field": "temperature", // если payload JSON; пусто — raw string
* "format": "%+.1f°C", // printf для extracted значения
* "anchor": "right-bottom", // right-top, left-bottom, ...
* "margin_x": 32, "margin_y": 24,
* "pixel_size": 32,
* "color": [255, 255, 255], "alpha": 230,
* "font_path": "/fonts/DejaVuSans-Bold.ttf"
* }, ...
* ]
* }
*
* Менеджер (MqttOverlayManager) держит vector<MqttOverlayItem>, поднимает
* MQTT-клиентов и добавляет overlays в композер. Hot-reload через
* reload_from_file() — пересоздаёт всех subscribers и overlays.
*
* Лицензия: LGPL-2.1+
*/
#ifndef CUFRAMES_COMPOSER_CPP_MQTT_OVERLAY_HPP
#define CUFRAMES_COMPOSER_CPP_MQTT_OVERLAY_HPP
#include "../overlay.h"
#include <atomic>
#include <memory>
#include <string>
#include <vector>
struct mosquitto;
struct mosquitto_message;
namespace cfc {
struct MqttOverlayCfg {
std::string id;
std::string topic;
std::string json_field; /* если payload JSON; пусто = raw string */
std::string format = "%s"; /* printf-formatted (для double — "%+.1f°C") */
std::string anchor = "right-bottom"; /* right-top, left-bottom, ... */
int margin_x = 32, margin_y = 24;
int pixel_size = 32;
int r = 255, g = 255, b = 255;
int alpha = 230;
std::string font_path = "/fonts/DejaVuSans-Bold.ttf";
};
struct MqttBrokerCfg {
std::string host = "cctv-mosquitto";
int port = 1883;
std::string username;
std::string password;
};
class MqttOverlayItem {
public:
MqttOverlayItem(const MqttOverlayCfg& cfg, const MqttBrokerCfg& broker,
int frame_w, int frame_h);
~MqttOverlayItem();
MqttOverlayItem(const MqttOverlayItem&) = delete;
MqttOverlayItem& operator=(const MqttOverlayItem&) = delete;
bool start();
cfc_overlay_t* overlay() const { return overlay_; }
const std::string& id() const { return cfg_.id; }
private:
static void on_connect(struct mosquitto* m, void* user, int rc);
static void on_message(struct mosquitto* m, void* user,
const struct mosquitto_message* msg);
void handle_payload(const char* payload, std::size_t len);
void update_text(const std::string& text);
void reposition_overlay();
MqttOverlayCfg cfg_;
MqttBrokerCfg broker_;
int frame_w_, frame_h_;
struct mosquitto* mosq_ = nullptr;
cfc_overlay_t* overlay_ = nullptr;
std::atomic<bool> running_{false};
std::string last_text_;
};
class MqttOverlayManager {
public:
explicit MqttOverlayManager(const MqttBrokerCfg& broker) : broker_(broker) {}
~MqttOverlayManager() = default;
/* Загрузить overlays из JSON-файла. Возвращает количество созданных. */
int load_from_file(const std::string& path, int frame_w, int frame_h);
/* Pointers на overlays для регистрации в композере. */
std::vector<cfc_overlay_t*> overlay_handles() const;
void clear();
int size() const { return static_cast<int>(items_.size()); }
private:
MqttBrokerCfg broker_;
std::vector<std::unique_ptr<MqttOverlayItem>> items_;
};
} // namespace cfc
#endif /* CUFRAMES_COMPOSER_CPP_MQTT_OVERLAY_HPP */