You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
3.4 KiB

3 years ago
  1. package lifx
  2. import (
  3. "context"
  4. "fmt"
  5. "git.aiterp.net/lucifer/new-server/models"
  6. "net"
  7. "sync"
  8. "time"
  9. )
  10. type Driver struct {
  11. mu sync.Mutex
  12. bridges []*Bridge
  13. }
  14. func (d *Driver) SearchBridge(ctx context.Context, address, _ string, _ bool) ([]models.Bridge, error) {
  15. if address == "" {
  16. ifaces, err := net.Interfaces()
  17. if err != nil {
  18. return nil, err
  19. }
  20. bridges := make([]models.Bridge, 0, len(ifaces))
  21. for _, iface := range ifaces {
  22. if iface.Name == "lo" {
  23. continue
  24. }
  25. addrs, err := iface.Addrs()
  26. if err != nil || len(addrs) == 0 {
  27. continue
  28. }
  29. for _, addr := range addrs {
  30. bridges = append(bridges, models.Bridge{
  31. ID: -1,
  32. Name: fmt.Sprintf("%s (%s)", iface.Name, addr),
  33. Driver: models.DTLIFX,
  34. Address: addr.String(),
  35. })
  36. }
  37. }
  38. return bridges, nil
  39. }
  40. ctx2, cancel := context.WithCancel(ctx)
  41. defer cancel()
  42. _, err := createClient(ctx2, address, false)
  43. if err != nil {
  44. return nil, err
  45. }
  46. return []models.Bridge{{
  47. ID: 0,
  48. Name: "Your network card",
  49. Driver: models.DTLIFX,
  50. Address: address,
  51. Token: "",
  52. }}, nil
  53. }
  54. func (d *Driver) SearchDevices(ctx context.Context, bridge models.Bridge, timeout time.Duration) ([]models.Device, error) {
  55. b, err := d.ensureBridge(ctx, bridge)
  56. if err != nil {
  57. return nil, err
  58. }
  59. before, err := d.ListDevices(ctx, bridge)
  60. if err != nil {
  61. return nil, err
  62. }
  63. err = b.StartSearch(ctx)
  64. if err != nil {
  65. return nil, err
  66. }
  67. if timeout < time.Second/10 {
  68. timeout = time.Second / 10
  69. }
  70. select {
  71. case <-ctx.Done():
  72. return nil, ctx.Err()
  73. case <-time.After(timeout):
  74. }
  75. after, err := d.ListDevices(ctx, bridge)
  76. if err != nil {
  77. return nil, err
  78. }
  79. intersection := make([]models.Device, 0, 4)
  80. for _, device := range after {
  81. found := false
  82. for _, device2 := range before {
  83. if device2.InternalID == device.InternalID {
  84. found = true
  85. break
  86. }
  87. }
  88. if !found {
  89. intersection = append(intersection, device)
  90. }
  91. }
  92. return intersection, nil
  93. }
  94. func (d *Driver) ListDevices(ctx context.Context, bridge models.Bridge) ([]models.Device, error) {
  95. b, err := d.ensureBridge(ctx, bridge)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return b.Devices(), nil
  100. }
  101. func (d *Driver) Publish(ctx context.Context, bridge models.Bridge, devices []models.Device) error {
  102. b, err := d.ensureBridge(ctx, bridge)
  103. if err != nil {
  104. return err
  105. }
  106. b.Publish(devices)
  107. return nil
  108. }
  109. func (d *Driver) Run(ctx context.Context, bridge models.Bridge, ch chan<- models.Event) error {
  110. b, err := d.ensureBridge(ctx, bridge)
  111. if err != nil {
  112. return err
  113. }
  114. return b.Run(ctx, bridge.Token == "debug")
  115. }
  116. func (d *Driver) ensureBridge(ctx context.Context, info models.Bridge) (*Bridge, error) {
  117. d.mu.Lock()
  118. for _, bridge := range d.bridges {
  119. if bridge.ip == info.Address {
  120. d.mu.Unlock()
  121. return bridge, nil
  122. }
  123. }
  124. d.mu.Unlock()
  125. bridge := &Bridge{
  126. ip: info.Address,
  127. externalID: info.ID,
  128. }
  129. // Try to create a short-lived client to make sure it works.
  130. ctx2, cancel := context.WithCancel(ctx)
  131. defer cancel()
  132. _, err := createClient(ctx2, info.Address, info.Token == "debug")
  133. if err != nil {
  134. return nil, err
  135. }
  136. // To avoid a potential duplicate, try looking for it again before inserting
  137. d.mu.Lock()
  138. for _, bridge := range d.bridges {
  139. if bridge.ip == info.Address {
  140. d.mu.Unlock()
  141. return bridge, nil
  142. }
  143. }
  144. d.bridges = append(d.bridges, bridge)
  145. d.mu.Unlock()
  146. return bridge, nil
  147. }