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.

118 lines
2.5 KiB

  1. package tradfri
  2. import (
  3. "fmt"
  4. lucifer3 "git.aiterp.net/lucifer3/server"
  5. "git.aiterp.net/lucifer3/server/commands"
  6. "git.aiterp.net/lucifer3/server/events"
  7. "github.com/sirupsen/logrus"
  8. "io"
  9. "strings"
  10. "sync"
  11. )
  12. func NewService() lucifer3.ActiveService {
  13. logrus.StandardLogger().Out = io.Discard
  14. logrus.StandardLogger().ExitFunc = func(i int) {
  15. panic(fmt.Sprintf("tradfri: exit code %d", i))
  16. }
  17. return &service{
  18. bridges: make(map[string]*Bridge, 4),
  19. }
  20. }
  21. type service struct {
  22. mu sync.Mutex
  23. bridges map[string]*Bridge
  24. }
  25. func (s *service) Active() bool {
  26. return true
  27. }
  28. func (s *service) HandleEvent(*lucifer3.EventBus, lucifer3.Event) {
  29. // NOP
  30. }
  31. func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  32. defer s.mu.Unlock()
  33. s.mu.Lock()
  34. switch command := command.(type) {
  35. case commands.PairDevice:
  36. if sub, ok := command.Matches("tradfri"); ok {
  37. bridge, err := connect(sub, command.APIKey)
  38. if err != nil {
  39. bus.RunEvent(events.DeviceFailed{
  40. ID: command.ID,
  41. Error: err.Error(),
  42. })
  43. return
  44. }
  45. s.bridges[bridge.id] = bridge
  46. bus.RunEvent(events.DeviceAccepted{
  47. ID: bridge.id,
  48. APIKey: bridge.newCredentials,
  49. })
  50. }
  51. case commands.SearchDevices:
  52. bus.RunEvent(events.DeviceFailed{
  53. ID: command.ID,
  54. Error: "Please follow instructions in IKEA app; they will appear here after pairing.",
  55. })
  56. case commands.SetState:
  57. if _, ok := command.Matches("tradfri"); ok {
  58. bridge, ok := s.findBridge(command.ID)
  59. if !ok {
  60. return
  61. }
  62. bridge.writeState(command.ID, command.State, bus)
  63. }
  64. case commands.SetStateBatch:
  65. for id, state := range command {
  66. if strings.HasPrefix(id, "tradfri") {
  67. bridge, ok := s.findBridge(id)
  68. if !ok {
  69. return
  70. }
  71. bridge.writeState(id, state, bus)
  72. }
  73. }
  74. case commands.ConnectDevice:
  75. if sub, ok := command.Matches("tradfri"); ok {
  76. if s.bridges[command.ID] == nil {
  77. bridge, err := connect(sub, command.APIKey)
  78. if err != nil {
  79. bus.RunEvent(events.DeviceFailed{
  80. ID: command.ID,
  81. Error: err.Error(),
  82. })
  83. return
  84. }
  85. s.bridges[command.ID] = bridge
  86. }
  87. bus.RunEvent(events.DeviceConnected{Prefix: s.bridges[command.ID].id})
  88. s.bridges[command.ID].listen(bus)
  89. }
  90. }
  91. }
  92. func (s *service) findBridge(id string) (*Bridge, bool) {
  93. bits := strings.Split(id, ":")
  94. for i := 2; i < len(bits); i++ {
  95. prefix := strings.Join(bits[0:i], ":")
  96. if s.bridges[prefix] != nil {
  97. return s.bridges[prefix], true
  98. }
  99. }
  100. return nil, false
  101. }