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.

145 lines
2.8 KiB

  1. package mill
  2. import (
  3. "errors"
  4. lucifer3 "git.aiterp.net/lucifer3/server"
  5. "git.aiterp.net/lucifer3/server/commands"
  6. "git.aiterp.net/lucifer3/server/device"
  7. "git.aiterp.net/lucifer3/server/events"
  8. "sync"
  9. )
  10. func NewService() lucifer3.ActiveService {
  11. return &service{
  12. bridges: make([]Bridge, 0, 8),
  13. }
  14. }
  15. type service struct {
  16. mu sync.Mutex
  17. bridges []Bridge
  18. }
  19. func (s *service) Active() bool {
  20. return true
  21. }
  22. func (s *service) HandleEvent(*lucifer3.EventBus, lucifer3.Event) {
  23. // NOP
  24. }
  25. func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  26. defer s.mu.Unlock()
  27. s.mu.Lock()
  28. switch cmd := command.(type) {
  29. case commands.PairDevice:
  30. // Only mill
  31. if _, ok := cmd.Matches("mill"); !ok {
  32. return
  33. }
  34. // Run connection if needed
  35. err := s.connect(bus, cmd.ID, &cmd.APIKey, nil)
  36. if err != nil {
  37. bus.RunEvent(events.DeviceFailed{
  38. ID: cmd.ID,
  39. Error: err.Error(),
  40. })
  41. return
  42. }
  43. // Connection successful
  44. bus.RunEvent(events.DeviceAccepted{
  45. ID: cmd.ID,
  46. APIKey: cmd.APIKey,
  47. })
  48. case commands.ConnectDevice:
  49. // Connect if necessary
  50. err := s.connect(bus, cmd.ID, &cmd.APIKey, nil)
  51. if err != nil {
  52. bus.RunEvent(events.DeviceFailed{
  53. ID: cmd.ID,
  54. Error: err.Error(),
  55. })
  56. return
  57. }
  58. case commands.SetState:
  59. err := s.connect(bus, cmd.ID, nil, &cmd.State)
  60. if err != nil {
  61. bus.RunEvent(events.DeviceFailed{
  62. ID: cmd.ID,
  63. Error: err.Error(),
  64. })
  65. return
  66. }
  67. case commands.SetStateBatch:
  68. for id, state := range cmd {
  69. err := s.connect(bus, id, nil, &state)
  70. if err != nil {
  71. bus.RunEvent(events.DeviceFailed{
  72. ID: id,
  73. Error: err.Error(),
  74. })
  75. return
  76. }
  77. }
  78. }
  79. }
  80. func (s *service) connect(
  81. bus *lucifer3.EventBus,
  82. id string,
  83. apiKey *string,
  84. state *device.State,
  85. ) error {
  86. hasState := state != nil
  87. if !hasState {
  88. state = &device.State{}
  89. }
  90. // Check if connection is active
  91. for _, bridge := range s.bridges {
  92. // Don't block with dead bridges
  93. if !bridge.IsStarted() {
  94. continue
  95. }
  96. // Setting a dummy state to the bridge to check if it's online
  97. bridge.SetBus(bus)
  98. if ok := bridge.SetState(id, *state); ok {
  99. return nil
  100. }
  101. }
  102. if apiKey == nil {
  103. return errors.New("not connected to device " + id)
  104. }
  105. // Make a bridge, if not a mill ID, this will return ok false
  106. bridge, ok := MakeBridge(id, *apiKey)
  107. if !ok {
  108. return nil
  109. }
  110. bridge.SetBus(bus)
  111. // Start bridge, and throw error if it failed to start
  112. bridge.Start()
  113. if !bridge.IsStarted() {
  114. return errors.New("failed to connect to " + id)
  115. }
  116. // Add bridge to lists
  117. s.bridges = append(s.bridges, bridge)
  118. // Update state, if needed, after first connection
  119. if hasState {
  120. if ok := bridge.SetState(id, *state); !ok {
  121. return errors.New("unable to set state to " + id + " after connecting")
  122. }
  123. }
  124. return nil
  125. }