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.

117 lines
2.6 KiB

  1. package hue2
  2. import (
  3. "context"
  4. "encoding/json"
  5. "encoding/xml"
  6. "fmt"
  7. "git.aiterp.net/lucifer/new-server/internal/lerrors"
  8. "git.aiterp.net/lucifer/new-server/models"
  9. "net/http"
  10. "sync"
  11. "time"
  12. )
  13. type Driver struct {
  14. mu sync.Mutex
  15. bridges []*Bridge
  16. }
  17. func (d *Driver) SearchBridge(ctx context.Context, address, token string, dryRun bool) ([]models.Bridge, error) {
  18. if address == "" {
  19. if !dryRun {
  20. return nil, lerrors.ErrAddressOnlyDryRunnable
  21. }
  22. res, err := http.Get("https://discovery.meethue.com")
  23. if err != nil {
  24. return nil, err
  25. }
  26. defer res.Body.Close()
  27. entries := make([]DiscoveryEntry, 0, 8)
  28. err = json.NewDecoder(res.Body).Decode(&entries)
  29. if err != nil {
  30. return nil, err
  31. }
  32. bridges := make([]models.Bridge, 0, len(entries))
  33. for _, entry := range entries {
  34. bridges = append(bridges, models.Bridge{
  35. ID: -1,
  36. Name: entry.Id,
  37. Driver: models.DTHue2,
  38. Address: entry.InternalIPAddress,
  39. Token: "",
  40. })
  41. }
  42. return bridges, nil
  43. }
  44. deviceInfo := BridgeDeviceInfo{}
  45. res, err := http.Get(fmt.Sprintf("http://%s/description.xml", address))
  46. if err != nil {
  47. return nil, err
  48. }
  49. defer res.Body.Close()
  50. err = xml.NewDecoder(res.Body).Decode(&deviceInfo)
  51. if err != nil {
  52. return nil, err
  53. }
  54. bridge := models.Bridge{
  55. ID: -1,
  56. Name: deviceInfo.Device.FriendlyName,
  57. Driver: models.DTHue2,
  58. Address: address,
  59. Token: "",
  60. }
  61. if !dryRun {
  62. client := NewClient(address, "")
  63. timeout, cancel := context.WithTimeout(ctx, time.Second*30)
  64. defer cancel()
  65. bridge.Token, err = client.Register(timeout)
  66. if err != nil {
  67. return nil, err
  68. }
  69. }
  70. return []models.Bridge{bridge}, nil
  71. }
  72. func (d *Driver) SearchDevices(ctx context.Context, bridge models.Bridge, timeout time.Duration) ([]models.Device, error) {
  73. return d.ensureBridge(bridge).SearchDevices(ctx, timeout)
  74. }
  75. func (d *Driver) ListDevices(_ context.Context, bridge models.Bridge) ([]models.Device, error) {
  76. return d.ensureBridge(bridge).GenerateDevices(), nil
  77. }
  78. func (d *Driver) Publish(_ context.Context, bridge models.Bridge, devices []models.Device) error {
  79. d.ensureBridge(bridge).Update(devices...)
  80. return nil
  81. }
  82. func (d *Driver) Run(ctx context.Context, bridge models.Bridge, ch chan<- models.Event) error {
  83. return d.ensureBridge(bridge).Run(ctx, ch)
  84. }
  85. func (d *Driver) ensureBridge(info models.Bridge) *Bridge {
  86. d.mu.Lock()
  87. for _, bridge := range d.bridges {
  88. if bridge.client.host == info.Address {
  89. d.mu.Unlock()
  90. return bridge
  91. }
  92. }
  93. bridge := NewBridge(NewClient(info.Address, info.Token))
  94. bridge.externalID = info.ID
  95. d.bridges = append(d.bridges, bridge)
  96. d.mu.Unlock()
  97. return bridge
  98. }