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.

123 lines
2.7 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/uistate"
  8. "github.com/labstack/echo/v4"
  9. "github.com/labstack/echo/v4/middleware"
  10. "log"
  11. "net"
  12. "sync"
  13. )
  14. func New(addr string) (lucifer3.Service, error) {
  15. svc := &service{}
  16. e := echo.New()
  17. e.HideBanner = true
  18. e.HidePort = true
  19. e.Use(middleware.CORS())
  20. e.GET("/state", func(c echo.Context) error {
  21. svc.mu.Lock()
  22. data := svc.data
  23. svc.mu.Unlock()
  24. return c.JSON(200, data)
  25. })
  26. e.POST("/command", func(c echo.Context) error {
  27. var input commandInput
  28. err := c.Bind(&input)
  29. if err != nil {
  30. return err
  31. }
  32. svc.mu.Lock()
  33. bus := svc.bus
  34. svc.mu.Unlock()
  35. if bus == nil {
  36. return c.String(413, "Waiting for bus")
  37. }
  38. switch {
  39. case input.Assign != nil:
  40. bus.RunCommand(commands.Assign{
  41. ID: nil,
  42. Match: input.Assign.Match,
  43. Effect: input.Assign.Effect.Effect,
  44. })
  45. case input.PairDevice != nil:
  46. bus.RunCommand(*input.PairDevice)
  47. case input.ForgetDevice != nil:
  48. bus.RunCommand(*input.ForgetDevice)
  49. case input.SearchDevices != nil:
  50. bus.RunCommand(*input.SearchDevices)
  51. case input.AddAlias != nil:
  52. bus.RunCommand(*input.AddAlias)
  53. case input.RemoveAlias != nil:
  54. bus.RunCommand(*input.RemoveAlias)
  55. default:
  56. return c.String(400, "No supported command found in input")
  57. }
  58. return c.JSON(200, input)
  59. })
  60. listener, err := net.Listen("tcp", addr)
  61. if err != nil {
  62. return nil, err
  63. }
  64. e.Listener = listener
  65. go func() {
  66. err := e.Start(addr)
  67. if err != nil {
  68. log.Fatalln("Failed to listen to webserver")
  69. }
  70. }()
  71. return svc, nil
  72. }
  73. type service struct {
  74. mu sync.Mutex
  75. data uistate.Data
  76. bus *lucifer3.EventBus
  77. }
  78. func (s *service) Active() bool {
  79. return true
  80. }
  81. func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
  82. switch event := event.(type) {
  83. case events.Started:
  84. s.mu.Lock()
  85. s.bus = bus
  86. s.mu.Unlock()
  87. case uistate.Patch:
  88. s.mu.Lock()
  89. s.data = s.data.WithPatch(event)
  90. s.mu.Unlock()
  91. // TODO: Broadcast websockets
  92. }
  93. }
  94. type commandInput struct {
  95. Assign *assignInput `json:"assign,omitempty"`
  96. AddAlias *commands.AddAlias `json:"addAlias,omitempty"`
  97. RemoveAlias *commands.RemoveAlias `json:"removeAlias,omitempty"`
  98. PairDevice *commands.PairDevice `json:"pairDevice,omitempty"`
  99. SearchDevices *commands.SearchDevices `json:"searchDevices,omitempty"`
  100. ForgetDevice *commands.ForgetDevice `json:"forgetDevice,omitempty"`
  101. }
  102. type assignInput struct {
  103. Match string `json:"match"`
  104. Effect effects.Serializable `json:"effect"`
  105. }