package bridge import ( "fmt" "log/slog" "github.com/AFASystems/presence/internal/pkg/config" mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/google/uuid" ) const defaultMQTTPort = 1883 const subscribeTopic = "publish_out/#" const disconnectQuiesceMs = 250 // MQTTClient wraps paho MQTT client and options. type MQTTClient struct { Client mqtt.Client } // NewMQTTClient creates and connects an MQTT client. Returns error instead of panic on connect failure. func NewMQTTClient(cfg *config.Config, publishHandler func(mqtt.Message)) (*MQTTClient, error) { opts := mqtt.NewClientOptions() opts.AddBroker(fmt.Sprintf("tcp://%s:%d", cfg.MQTTHost, defaultMQTTPort)) opts.SetClientID(fmt.Sprintf("bridge-%s", uuid.New().String())) opts.SetAutoReconnect(true) opts.SetConnectRetry(true) opts.SetConnectRetryInterval(config.SMALL_TICKER_INTERVAL) opts.SetMaxReconnectInterval(config.LARGE_TICKER_INTERVAL) opts.SetCleanSession(false) opts.SetDefaultPublishHandler(func(c mqtt.Client, m mqtt.Message) { publishHandler(m) }) opts.OnConnect = func(client mqtt.Client) { slog.Info("MQTT connected") } opts.OnConnectionLost = func(client mqtt.Client, err error) { slog.Error("MQTT connection lost", "err", err) } client := mqtt.NewClient(opts) token := client.Connect() token.Wait() if err := token.Error(); err != nil { return nil, fmt.Errorf("mqtt connect: %w", err) } return &MQTTClient{Client: client}, nil } // Subscribe subscribes to the default bridge topic. func (m *MQTTClient) Subscribe() { token := m.Client.Subscribe(subscribeTopic, 1, nil) token.Wait() slog.Info("MQTT subscribed", "topic", subscribeTopic) } // Disconnect disconnects the client with quiesce. func (m *MQTTClient) Disconnect() { m.Client.Disconnect(disconnectQuiesceMs) slog.Info("MQTT disconnected") }