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.

133 lines
3.0 KiB

2 years ago
  1. package nanoleaf
  2. import (
  3. "context"
  4. "fmt"
  5. lucifer3 "git.aiterp.net/lucifer3/server"
  6. "git.aiterp.net/lucifer3/server/commands"
  7. "git.aiterp.net/lucifer3/server/events"
  8. "strings"
  9. "time"
  10. )
  11. func NewService() lucifer3.ActiveService {
  12. return &service{
  13. bridges: make(map[string]*bridge),
  14. cancels: make(map[string]context.CancelFunc),
  15. }
  16. }
  17. type service struct {
  18. bridges map[string]*bridge
  19. cancels map[string]context.CancelFunc
  20. }
  21. func (s *service) Active() bool {
  22. return true
  23. }
  24. func (s *service) HandleEvent(*lucifer3.EventBus, lucifer3.Event) {}
  25. func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  26. switch command := command.(type) {
  27. case commands.SetState:
  28. if sub, ok := command.Matches("nanoleaf"); ok && s.bridges[sub] != nil {
  29. s.bridges[sub].Update(command.ID, command.State)
  30. }
  31. case commands.SetStateBatch:
  32. for _, b := range s.bridges {
  33. b.UpdateBatch(command)
  34. }
  35. case commands.SearchDevices:
  36. if sub, ok := command.Matches("nanoleaf"); ok {
  37. if s.bridges[sub] != nil {
  38. go func(bridge *bridge) {
  39. changed, err := bridge.Refresh(context.Background())
  40. if err != nil {
  41. bus.RunEvent(events.DeviceFailed{
  42. ID: command.ID,
  43. Error: fmt.Sprintf("Search failed: %s", err),
  44. })
  45. return
  46. }
  47. if changed {
  48. for _, event := range bridge.HardwareEvents() {
  49. bus.RunEvent(event)
  50. }
  51. }
  52. }(s.bridges[sub])
  53. }
  54. }
  55. case commands.PairDevice:
  56. if address, ok := command.Matches("nanoleaf"); ok {
  57. go func() {
  58. timeout, cancel := context.WithTimeout(context.Background(), time.Second*5)
  59. defer cancel()
  60. apiKey, info, err := Discover(timeout, address, true)
  61. if err != nil {
  62. bus.RunEvent(events.DeviceFailed{
  63. ID: command.ID,
  64. Error: fmt.Sprintf("Pairing failed: %s", err),
  65. })
  66. return
  67. }
  68. bus.RunEvent(events.DeviceAccepted{
  69. ID: command.ID,
  70. APIKey: apiKey,
  71. Extras: nil,
  72. })
  73. bus.RunEvent(events.HardwareMetadata{
  74. ID: command.ID,
  75. Icon: "bridge",
  76. SerialNumber: info.SerialNumber,
  77. FirmwareVersion: strings.Join([]string{
  78. info.FirmwareVersion, info.BootloaderVersion, info.HardwareVersion,
  79. }, "; "),
  80. })
  81. }()
  82. }
  83. case commands.ConnectDevice:
  84. if sub, ok := command.Matches("nanoleaf"); ok {
  85. if s.bridges[sub] != nil {
  86. s.cancels[sub]()
  87. }
  88. ctx, cancel := context.WithCancel(context.Background())
  89. s.bridges[sub] = &bridge{
  90. host: sub,
  91. apiKey: command.APIKey,
  92. panels: make([]*panel, 0, 64),
  93. panelIDMap: make(map[uint16]string),
  94. }
  95. s.cancels[sub] = cancel
  96. go func() {
  97. for ctx.Err() == nil {
  98. ctx2, cancel2 := context.WithCancel(ctx)
  99. err := s.bridges[sub].Run(ctx2, bus)
  100. cancel2()
  101. if err != nil {
  102. bus.RunEvent(events.DeviceFailed{
  103. ID: command.ID,
  104. Error: fmt.Sprintf("Run failed: %s", err),
  105. })
  106. }
  107. select {
  108. case <-time.After(time.Second * 5):
  109. case <-ctx.Done():
  110. return
  111. }
  112. }
  113. }()
  114. }
  115. }
  116. }