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

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-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 zigbee2mqtt
    20  
    21  import (
    22  	"context"
    23  	"sync"
    24  
    25  	"github.com/e154/smart-home/common/events"
    26  	"github.com/e154/smart-home/system/bus"
    27  
    28  	"github.com/e154/smart-home/common/apperr"
    29  
    30  	"github.com/e154/smart-home/common/logger"
    31  
    32  	"github.com/e154/smart-home/adaptors"
    33  	"github.com/e154/smart-home/common"
    34  	m "github.com/e154/smart-home/models"
    35  	"github.com/e154/smart-home/system/mqtt"
    36  	"go.uber.org/atomic"
    37  	"go.uber.org/fx"
    38  )
    39  
    40  var (
    41  	log = logger.MustGetLogger("zigbee2mqtt")
    42  )
    43  
    44  // zigbee2mqtt ...
    45  type zigbee2mqtt struct {
    46  	mqtt        mqtt.MqttServ
    47  	adaptors    *adaptors.Adaptors
    48  	isStarted   *atomic.Bool
    49  	bridgesLock *sync.Mutex
    50  	bridges     map[int64]*Bridge
    51  	eventBus    bus.Bus
    52  }
    53  
    54  // NewZigbee2mqtt ...
    55  func NewZigbee2mqtt(lc fx.Lifecycle,
    56  	mqtt mqtt.MqttServ,
    57  	adaptors *adaptors.Adaptors,
    58  	eventBus bus.Bus) Zigbee2mqtt {
    59  	zigbee2mqtt := &zigbee2mqtt{
    60  		mqtt:        mqtt,
    61  		adaptors:    adaptors,
    62  		bridgesLock: &sync.Mutex{},
    63  		bridges:     make(map[int64]*Bridge),
    64  		eventBus:    eventBus,
    65  		isStarted:   atomic.NewBool(false),
    66  	}
    67  
    68  	lc.Append(fx.Hook{
    69  		OnStart: zigbee2mqtt.Start,
    70  		OnStop:  zigbee2mqtt.Shutdown,
    71  	})
    72  	return zigbee2mqtt
    73  }
    74  
    75  // Start ...
    76  func (z *zigbee2mqtt) Start(ctx context.Context) (err error) {
    77  	if z.isStarted.Load() {
    78  		return
    79  	}
    80  	z.isStarted.Store(true)
    81  
    82  	models, _, err := z.adaptors.Zigbee2mqtt.List(ctx, 99, 0)
    83  	if err != nil {
    84  		log.Error(err.Error())
    85  	}
    86  
    87  	if len(models) == 0 {
    88  		model := &m.Zigbee2mqtt{
    89  			Name:       "zigbee2mqtt",
    90  			BaseTopic:  "zigbee2mqtt",
    91  			PermitJoin: true,
    92  		}
    93  		model.Id, err = z.adaptors.Zigbee2mqtt.Add(ctx, model)
    94  		if err != nil {
    95  			log.Error(err.Error())
    96  			return
    97  		}
    98  		models = append(models, model)
    99  	}
   100  
   101  	if err := z.mqtt.Authenticator().Register(z.Authenticator); err != nil {
   102  		log.Error(err.Error())
   103  	}
   104  
   105  	//todo fix race condition
   106  	for _, model := range models {
   107  		bridge := NewBridge(z.mqtt, z.adaptors, model)
   108  		bridge.Start()
   109  
   110  		z.bridgesLock.Lock()
   111  		z.bridges[model.Id] = bridge
   112  		z.bridgesLock.Unlock()
   113  	}
   114  
   115  	_ = z.eventBus.Subscribe("system/models/zigbee2mqtt/+", z.eventHandler)
   116  
   117  	z.eventBus.Publish("system/services/zigbee2mqtt", events.EventServiceStarted{Service: "Zigbee2mqtt"})
   118  
   119  	return
   120  }
   121  
   122  // Shutdown ...
   123  func (z *zigbee2mqtt) Shutdown(ctx context.Context) (err error) {
   124  	if !z.isStarted.Load() {
   125  		return
   126  	}
   127  	z.isStarted.Store(false)
   128  	for _, bridge := range z.bridges {
   129  		bridge.Stop(context.Background())
   130  	}
   131  	_ = z.mqtt.Authenticator().Unregister(z.Authenticator)
   132  
   133  	_ = z.eventBus.Unsubscribe("system/models/zigbee2mqtt/+", z.eventHandler)
   134  
   135  	z.eventBus.Publish("system/services/zigbee2mqtt", events.EventServiceStopped{Service: "Zigbee2mqtt"})
   136  
   137  	return
   138  }
   139  
   140  // AddBridge ...
   141  func (z *zigbee2mqtt) AddBridge(model *m.Zigbee2mqtt) (err error) {
   142  
   143  	z.bridgesLock.Lock()
   144  	defer z.bridgesLock.Unlock()
   145  
   146  	bridge := NewBridge(z.mqtt, z.adaptors, model)
   147  	bridge.Start()
   148  	z.bridges[model.Id] = bridge
   149  	return
   150  }
   151  
   152  // GetBridgeInfo ...
   153  func (z *zigbee2mqtt) GetBridgeInfo(id int64) (*m.Zigbee2mqttInfo, error) {
   154  	z.bridgesLock.Lock()
   155  	defer z.bridgesLock.Unlock()
   156  
   157  	if br, ok := z.bridges[id]; ok {
   158  		return br.Info(), nil
   159  	}
   160  	return nil, apperr.ErrNotFound
   161  }
   162  
   163  // UpdateBridge ...
   164  func (z *zigbee2mqtt) UpdateBridge(model *m.Zigbee2mqtt) (err error) {
   165  	z.bridgesLock.Lock()
   166  	defer z.bridgesLock.Unlock()
   167  
   168  	var bridge *Bridge
   169  	if bridge, err = z.unsafeGetBridge(model.Id); err == nil {
   170  		bridge.Stop(context.Background())
   171  		delete(z.bridges, model.Id)
   172  	} else {
   173  		return
   174  	}
   175  
   176  	bridge = NewBridge(z.mqtt, z.adaptors, model)
   177  	bridge.Start()
   178  	z.bridges[model.Id] = bridge
   179  
   180  	return
   181  }
   182  
   183  // DeleteBridge ...
   184  func (z *zigbee2mqtt) DeleteBridge(bridgeId int64) (err error) {
   185  	z.bridgesLock.Lock()
   186  	defer z.bridgesLock.Unlock()
   187  
   188  	var bridge *Bridge
   189  	if bridge, err = z.unsafeGetBridge(bridgeId); err == nil {
   190  		bridge.Stop(context.Background())
   191  		delete(z.bridges, bridgeId)
   192  	}
   193  
   194  	return
   195  }
   196  
   197  // ResetBridge ...
   198  func (z *zigbee2mqtt) ResetBridge(bridgeId int64) (err error) {
   199  	z.bridgesLock.Lock()
   200  	defer z.bridgesLock.Unlock()
   201  
   202  	var bridge *Bridge
   203  	if bridge, err = z.unsafeGetBridge(bridgeId); err == nil {
   204  		bridge.ConfigReset()
   205  	}
   206  	return
   207  }
   208  
   209  // BridgeDeviceBan ...
   210  func (z *zigbee2mqtt) BridgeDeviceBan(bridgeId int64, friendlyName string) (err error) {
   211  	z.bridgesLock.Lock()
   212  	defer z.bridgesLock.Unlock()
   213  
   214  	var bridge *Bridge
   215  	if bridge, err = z.unsafeGetBridge(bridgeId); err == nil {
   216  		bridge.Ban(friendlyName)
   217  	}
   218  	return
   219  }
   220  
   221  // BridgeDeviceWhitelist ...
   222  func (z *zigbee2mqtt) BridgeDeviceWhitelist(bridgeId int64, friendlyName string) (err error) {
   223  	z.bridgesLock.Lock()
   224  	defer z.bridgesLock.Unlock()
   225  
   226  	var bridge *Bridge
   227  	if bridge, err = z.unsafeGetBridge(bridgeId); err == nil {
   228  		bridge.Whitelist(friendlyName)
   229  	}
   230  	return
   231  }
   232  
   233  // BridgeNetworkmap ...
   234  func (z *zigbee2mqtt) BridgeNetworkmap(bridgeId int64) (networkmap string, err error) {
   235  	z.bridgesLock.Lock()
   236  	defer z.bridgesLock.Unlock()
   237  
   238  	var bridge *Bridge
   239  	if bridge, err = z.unsafeGetBridge(bridgeId); err == nil {
   240  		networkmap = bridge.Networkmap()
   241  	}
   242  	return
   243  }
   244  
   245  // BridgeUpdateNetworkmap ...
   246  func (z *zigbee2mqtt) BridgeUpdateNetworkmap(bridgeId int64) (err error) {
   247  	z.bridgesLock.Lock()
   248  	defer z.bridgesLock.Unlock()
   249  
   250  	var bridge *Bridge
   251  	if bridge, err = z.unsafeGetBridge(bridgeId); err == nil {
   252  		bridge.UpdateNetworkmap()
   253  	}
   254  	return
   255  }
   256  
   257  func (z *zigbee2mqtt) unsafeGetBridge(bridgeId int64) (bridge *Bridge, err error) {
   258  	var ok bool
   259  	if bridge, ok = z.bridges[bridgeId]; !ok {
   260  		err = apperr.ErrNotFound
   261  	}
   262  	return
   263  }
   264  
   265  // GetTopicByDevice ...
   266  func (z *zigbee2mqtt) GetTopicByDevice(model *m.Zigbee2mqttDevice) (topic string, err error) {
   267  
   268  	z.bridgesLock.Lock()
   269  	defer z.bridgesLock.Unlock()
   270  
   271  	br, ok := z.bridges[model.Zigbee2mqttId]
   272  	if !ok {
   273  		err = apperr.ErrNotFound
   274  		return
   275  	}
   276  
   277  	topic = br.GetDeviceTopic(model.Id)
   278  
   279  	return
   280  }
   281  
   282  // DeviceRename ...
   283  func (z *zigbee2mqtt) DeviceRename(friendlyName, name string) (err error) {
   284  	z.bridgesLock.Lock()
   285  	defer z.bridgesLock.Unlock()
   286  
   287  	for _, bridge := range z.bridges {
   288  		_ = bridge.RenameDevice(friendlyName, name)
   289  	}
   290  
   291  	return
   292  }
   293  
   294  // Authenticator ...
   295  func (z *zigbee2mqtt) Authenticator(login, password string) (err error) {
   296  
   297  	z.bridgesLock.Lock()
   298  	defer z.bridgesLock.Unlock()
   299  
   300  	for _, bridge := range z.bridges {
   301  		if bridge.model.Login != login {
   302  			err = apperr.ErrBadLoginOrPassword
   303  			return
   304  		}
   305  
   306  		if ok := common.CheckPasswordHash(password, bridge.model.EncryptedPassword); ok {
   307  			err = nil
   308  			return
   309  		}
   310  	}
   311  
   312  	err = apperr.ErrBadLoginOrPassword
   313  
   314  	return
   315  }
   316  
   317  // eventHandler ...
   318  func (z *zigbee2mqtt) eventHandler(_ string, message interface{}) {
   319  
   320  	var err error
   321  	switch msg := message.(type) {
   322  	case events.EventCreatedZigbee2mqttModel:
   323  		err = z.AddBridge(msg.Bridge)
   324  	case events.EventUpdatedZigbee2mqttModel:
   325  		err = z.UpdateBridge(msg.Bridge)
   326  	case events.EventRemovedZigbee2mqttModel:
   327  		err = z.DeleteBridge(msg.Id)
   328  	}
   329  
   330  	if err != nil {
   331  		log.Error(err.Error())
   332  	}
   333  }