github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/plugins/mqtt_bridge/mqtt_bridge.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package mqtt_bridge
    20  
    21  import (
    22  	"context"
    23  	"time"
    24  
    25  	"github.com/e154/smart-home/common"
    26  	"github.com/e154/smart-home/system/supervisor"
    27  
    28  	MQTT "github.com/eclipse/paho.mqtt.golang"
    29  	"github.com/google/uuid"
    30  	"go.uber.org/atomic"
    31  
    32  	"github.com/e154/smart-home/system/mqtt"
    33  )
    34  
    35  type MqttBridge struct {
    36  	cfg          *Config
    37  	client       MQTT.Client
    38  	server       mqtt.MqttServ
    39  	serverClient mqtt.MqttCli
    40  	isStarted    *atomic.Bool
    41  	actor        *Actor
    42  }
    43  
    44  func NewMqttBridge(cfg *Config, server mqtt.MqttServ, actor *Actor) (bridge *MqttBridge, err error) {
    45  	bridge = &MqttBridge{
    46  		cfg:          cfg,
    47  		server:       server,
    48  		isStarted:    atomic.NewBool(false),
    49  		serverClient: server.NewClient(uuid.NewString()),
    50  		actor:        actor,
    51  	}
    52  	return
    53  }
    54  
    55  func (m *MqttBridge) Start(ctx context.Context) (err error) {
    56  	if m.isStarted.Load() {
    57  		return
    58  	}
    59  	m.isStarted.Store(true)
    60  	defer func() {
    61  		if err != nil {
    62  			m.isStarted.Store(false)
    63  		}
    64  	}()
    65  
    66  	opts := MQTT.NewClientOptions().
    67  		AddBroker(m.cfg.Broker).
    68  		SetClientID(m.cfg.ClientID).
    69  		SetKeepAlive(time.Duration(m.cfg.KeepAlive) * time.Second).
    70  		SetPingTimeout(time.Duration(m.cfg.PingTimeout) * time.Second).
    71  		SetConnectTimeout(time.Duration(m.cfg.ConnectTimeout) * time.Second).
    72  		SetMaxReconnectInterval(time.Minute).
    73  		SetAutoReconnect(true).
    74  		SetCleanSession(m.cfg.CleanSession).
    75  		SetOnConnectHandler(m.onConnect).
    76  		SetConnectionLostHandler(m.onConnectionLostHandler).
    77  		SetUsername(m.cfg.Username).
    78  		SetPassword(m.cfg.Password)
    79  	m.client = MQTT.NewClient(opts)
    80  	if token := m.client.Connect(); token.Wait() && token.Error() != nil {
    81  		log.Error(token.Error().Error())
    82  	}
    83  	return
    84  }
    85  
    86  func (m *MqttBridge) Shutdown(ctx context.Context) (err error) {
    87  	if !m.isStarted.Load() {
    88  		return
    89  	}
    90  	m.isStarted.Store(false)
    91  	if m.client != nil {
    92  		m.client.Disconnect(250)
    93  	}
    94  	return
    95  }
    96  
    97  func (m *MqttBridge) onConnect(client MQTT.Client) {
    98  
    99  	switch m.cfg.Direction {
   100  	case DirectionBoth:
   101  		m.directionBoth(client)
   102  	case DirectionIn:
   103  		m.directionIn(client)
   104  	case DirectionOut:
   105  		m.directionOut(client)
   106  	default:
   107  		log.Debugf("unknown direction '%s'", m.cfg.Direction)
   108  	}
   109  
   110  	m.actor.SetState(supervisor.EntityStateParams{
   111  		NewState:    common.String(AttrConnected),
   112  		StorageSave: true,
   113  	})
   114  
   115  	log.Debug("connected ...")
   116  }
   117  
   118  func (m *MqttBridge) onConnectionLostHandler(client MQTT.Client, e error) {
   119  
   120  	_ = m.actor.SetState(supervisor.EntityStateParams{
   121  		NewState:    common.String(AttrOffline),
   122  		StorageSave: true,
   123  	})
   124  
   125  	//if e != nil {
   126  	//	log.Debugf("connection lost... %s", e.Error())
   127  	//} else {
   128  	//	log.Debug("connection lost...")
   129  	//}
   130  
   131  	for _, topic := range m.cfg.Topics {
   132  		if token := m.client.Unsubscribe(topic); token.Error() != nil {
   133  			log.Error(token.Error().Error())
   134  		}
   135  	}
   136  	m.serverClient.UnsubscribeAll()
   137  }
   138  
   139  func (m *MqttBridge) directionBoth(client MQTT.Client) {
   140  
   141  	for _, topic := range m.cfg.Topics {
   142  		log.Debugf("subscribe: %s", topic)
   143  		if token := client.Subscribe(topic, m.cfg.Qos, m.clientMessageHandler); token.Wait() && token.Error() != nil {
   144  			log.Error(token.Error().Error())
   145  		}
   146  
   147  		if err := m.serverClient.Subscribe(topic, m.serverMessageHandler); err != nil {
   148  			log.Error(err.Error())
   149  		}
   150  	}
   151  }
   152  
   153  func (m *MqttBridge) directionIn(client MQTT.Client) {
   154  
   155  	for _, topic := range m.cfg.Topics {
   156  		//log.Debugf("subscribe: %s", topic)
   157  		if token := client.Subscribe(topic, m.cfg.Qos, m.clientMessageHandler); token.Wait() && token.Error() != nil {
   158  			log.Error(token.Error().Error())
   159  		}
   160  	}
   161  }
   162  
   163  func (m *MqttBridge) directionOut(client MQTT.Client) {
   164  
   165  	var err error
   166  	for _, topic := range m.cfg.Topics {
   167  		if err = m.serverClient.Subscribe(topic, m.serverMessageHandler); err != nil {
   168  			log.Error(err.Error())
   169  		}
   170  	}
   171  }
   172  
   173  func (m *MqttBridge) serverMessageHandler(client mqtt.MqttCli, message mqtt.Message) {
   174  	m.client.Publish(message.Topic, message.Qos, message.Retained, message.Payload)
   175  }
   176  
   177  func (m *MqttBridge) clientMessageHandler(_ MQTT.Client, message MQTT.Message) {
   178  	if err := m.server.Publish(message.Topic(), message.Payload(), message.Qos(), message.Retained()); err != nil {
   179  		log.Error(err.Error())
   180  	}
   181  }