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.

156 lines
3.9 KiB

2 years ago
2 years ago
  1. package httpapiv1
  2. import (
  3. lucifer3 "git.aiterp.net/lucifer3/server"
  4. "git.aiterp.net/lucifer3/server/commands"
  5. "git.aiterp.net/lucifer3/server/effects"
  6. "git.aiterp.net/lucifer3/server/events"
  7. "git.aiterp.net/lucifer3/server/services/script"
  8. "git.aiterp.net/lucifer3/server/services/uistate"
  9. "github.com/google/uuid"
  10. "github.com/labstack/echo/v4"
  11. "github.com/labstack/echo/v4/middleware"
  12. "log"
  13. "net"
  14. "sync"
  15. )
  16. var zeroUUID = uuid.UUID{
  17. 0, 0, 0, 0,
  18. 0, 0, 0, 0,
  19. 0, 0, 0, 0,
  20. 0, 0, 0, 0,
  21. }
  22. func New(addr string) (lucifer3.Service, error) {
  23. svc := &service{}
  24. e := echo.New()
  25. e.HideBanner = true
  26. e.HidePort = true
  27. e.Use(middleware.CORS())
  28. e.GET("/state", func(c echo.Context) error {
  29. svc.mu.Lock()
  30. data := svc.data
  31. svc.mu.Unlock()
  32. return c.JSON(200, data)
  33. })
  34. e.POST("/command", func(c echo.Context) error {
  35. var input commandInput
  36. err := c.Bind(&input)
  37. if err != nil {
  38. return err
  39. }
  40. svc.mu.Lock()
  41. bus := svc.bus
  42. svc.mu.Unlock()
  43. if bus == nil {
  44. return c.String(413, "Waiting for bus")
  45. }
  46. switch {
  47. case input.Assign != nil:
  48. bus.RunCommand(commands.Assign{
  49. ID: nil,
  50. Match: input.Assign.Match,
  51. Effect: input.Assign.Effect.Effect,
  52. })
  53. case input.PairDevice != nil:
  54. bus.RunCommand(*input.PairDevice)
  55. case input.ConnectDevice != nil:
  56. bus.RunCommand(*input.ConnectDevice)
  57. case input.ForgetDevice != nil:
  58. bus.RunCommand(*input.ForgetDevice)
  59. case input.SearchDevices != nil:
  60. bus.RunCommand(*input.SearchDevices)
  61. case input.AddAlias != nil:
  62. bus.RunCommand(*input.AddAlias)
  63. case input.RemoveAlias != nil:
  64. bus.RunCommand(*input.RemoveAlias)
  65. case input.UpdateScript != nil:
  66. bus.RunCommand(*input.UpdateScript)
  67. case input.ExecuteScript != nil:
  68. bus.RunCommand(*input.ExecuteScript)
  69. case input.UpdateTrigger != nil:
  70. if input.UpdateTrigger.ID == zeroUUID {
  71. input.UpdateTrigger.ID = uuid.New()
  72. }
  73. if input.UpdateTrigger.ScriptPre == nil {
  74. input.UpdateTrigger.ScriptPre = make([]script.Line, 0)
  75. }
  76. if input.UpdateTrigger.ScriptPost == nil {
  77. input.UpdateTrigger.ScriptPost = make([]script.Line, 0)
  78. }
  79. bus.RunCommand(*input.UpdateTrigger)
  80. case input.DeleteTrigger != nil:
  81. bus.RunCommand(*input.DeleteTrigger)
  82. default:
  83. return c.String(400, "No supported command found in input")
  84. }
  85. return c.JSON(200, input)
  86. })
  87. listener, err := net.Listen("tcp", addr)
  88. if err != nil {
  89. return nil, err
  90. }
  91. e.Listener = listener
  92. go func() {
  93. err := e.Start(addr)
  94. if err != nil {
  95. log.Fatalln("Failed to listen to webserver")
  96. }
  97. }()
  98. return svc, nil
  99. }
  100. type service struct {
  101. mu sync.Mutex
  102. data uistate.Data
  103. bus *lucifer3.EventBus
  104. }
  105. func (s *service) Active() bool {
  106. return true
  107. }
  108. func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
  109. switch event := event.(type) {
  110. case events.Started:
  111. s.mu.Lock()
  112. s.bus = bus
  113. s.mu.Unlock()
  114. case uistate.Patch:
  115. s.mu.Lock()
  116. s.data = s.data.WithPatch(event)
  117. s.mu.Unlock()
  118. // TODO: Broadcast websockets
  119. }
  120. }
  121. type commandInput struct {
  122. Assign *assignInput `json:"assign,omitempty"`
  123. AddAlias *commands.AddAlias `json:"addAlias,omitempty"`
  124. RemoveAlias *commands.RemoveAlias `json:"removeAlias,omitempty"`
  125. PairDevice *commands.PairDevice `json:"pairDevice,omitempty"`
  126. ConnectDevice *commands.ConnectDevice `json:"connectDevice,omitempty"`
  127. SearchDevices *commands.SearchDevices `json:"searchDevices,omitempty"`
  128. ForgetDevice *commands.ForgetDevice `json:"forgetDevice,omitempty"`
  129. UpdateScript *script.Update `json:"updateScript,omitempty"`
  130. ExecuteScript *script.Execute `json:"executeScript,omitempty"`
  131. UpdateTrigger *script.UpdateTrigger `json:"updateTrigger,omitempty"`
  132. DeleteTrigger *script.DeleteTrigger `json:"deleteTrigger,omitempty"`
  133. }
  134. type assignInput struct {
  135. Match string `json:"match"`
  136. Effect effects.Serializable `json:"effect"`
  137. }