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.

220 lines
5.3 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package services
  2. import (
  3. "context"
  4. "fmt"
  5. "git.aiterp.net/lucifer/new-server/app/config"
  6. "git.aiterp.net/lucifer/new-server/app/services/scene"
  7. "git.aiterp.net/lucifer/new-server/models"
  8. "log"
  9. "math"
  10. "sort"
  11. "strconv"
  12. "strings"
  13. "sync"
  14. "time"
  15. )
  16. func StartEventHandler() {
  17. config.EventChannel <- models.Event{Name: "LuciferStarted"}
  18. go func() {
  19. for event := range config.EventChannel {
  20. responses := handleEvent(event)
  21. for _, response := range responses {
  22. handleEvent(response)
  23. }
  24. }
  25. }()
  26. // Generate TimeChanged event
  27. go func() {
  28. drift := time.Now().Add(time.Minute).Truncate(time.Minute).Sub(time.Now())
  29. time.Sleep(drift + time.Millisecond*5)
  30. for range time.NewTicker(time.Minute).C {
  31. config.EventChannel <- models.Event{Name: "TimeChanged", Payload: map[string]string{
  32. "weekdayName": time.Now().In(loc).Format("Monday"),
  33. "month": time.Now().In(loc).Format("01"),
  34. "year": time.Now().In(loc).Format("2006"),
  35. }}
  36. }
  37. }()
  38. }
  39. var loc, _ = time.LoadLocation("Europe/Oslo")
  40. var ctx = context.Background()
  41. func handleEvent(event models.Event) (responses []models.Event) {
  42. var deadHandlers []models.EventHandler
  43. startTime := time.Now()
  44. defer func() {
  45. duration := time.Since(startTime)
  46. if duration > time.Millisecond*250 {
  47. log.Println(event.Name, "took long to handle:", duration)
  48. }
  49. }()
  50. if !event.HasPayload("hour") {
  51. event.AddPayload("hour", time.Now().In(loc).Format("15"))
  52. }
  53. if !event.HasPayload("minute") {
  54. event.AddPayload("minute", time.Now().In(loc).Format("04"))
  55. }
  56. err := handleSpecial(event)
  57. if err != nil {
  58. log.Printf("Special event handler error (%s): %v", event.Name, err)
  59. return
  60. }
  61. paramStrings := make([]string, 0, 8)
  62. for key, value := range event.Payload {
  63. paramStrings = append(paramStrings, fmt.Sprintf("%s=%s", key, value))
  64. }
  65. sort.Strings(paramStrings)
  66. log.Printf("Event %s(%s)", event.Name, strings.Join(paramStrings, ", "))
  67. handlers, err := config.EventHandlerRepository().FetchAll(ctx)
  68. if err != nil {
  69. log.Printf("Error fetchin' event halders: %s", err)
  70. return
  71. }
  72. allDevices := make([]models.Device, 0, 16)
  73. actions := make(map[int]models.EventAction, 16)
  74. priorities := make(map[int]int, 16)
  75. highestPriority := 0
  76. var prioritizedEvent *models.Event
  77. for _, handler := range handlers {
  78. if handler.EventName != event.Name {
  79. continue
  80. }
  81. devices, err := config.DeviceRepository().FetchByReference(ctx, handler.TargetKind, handler.TargetValue)
  82. if err != nil {
  83. log.Printf("Error fetchin' event devices: %s", err)
  84. return
  85. }
  86. if !handler.MatchesEvent(event, devices) {
  87. continue
  88. }
  89. if handler.OneShot {
  90. deadHandlers = append(deadHandlers, handler)
  91. }
  92. if handler.Priority > highestPriority {
  93. highestPriority = handler.Priority
  94. prioritizedEvent = handler.Actions.FireEvent
  95. }
  96. for _, device := range devices {
  97. _, ok := actions[device.ID]
  98. if !ok {
  99. allDevices = append(allDevices, device)
  100. actions[device.ID] = handler.Actions
  101. priorities[device.ID] = handler.Priority
  102. } else if handler.Priority > priorities[device.ID] {
  103. actions[device.ID] = handler.Actions
  104. priorities[device.ID] = handler.Priority
  105. }
  106. }
  107. }
  108. if prioritizedEvent != nil {
  109. responses = append(responses, *prioritizedEvent)
  110. }
  111. for i, device := range allDevices {
  112. action := actions[device.ID]
  113. newState := models.NewDeviceState{}
  114. newState.Color = action.SetColor
  115. newState.Power = action.SetPower
  116. if action.SetIntensity != nil {
  117. newState.Intensity = action.SetIntensity
  118. } else if action.AddIntensity != nil {
  119. newIntensity := math.Max(0, math.Min(1, device.State.Intensity+*action.AddIntensity))
  120. newState.Intensity = &newIntensity
  121. }
  122. err = allDevices[i].SetState(newState)
  123. if err != nil {
  124. log.Println("Error updating state for device", device.ID, "err:", err)
  125. }
  126. }
  127. config.PublishChannel <- scene.GlobalManager().FilterUnassigned(allDevices)
  128. wg := sync.WaitGroup{}
  129. if len(allDevices) > 0 {
  130. wg.Add(1)
  131. go func() {
  132. err := config.DeviceRepository().SaveMany(context.Background(), models.SMState, allDevices)
  133. if err != nil {
  134. log.Println("Failed to save devices' state:", err)
  135. }
  136. wg.Done()
  137. }()
  138. for _, device := range allDevices {
  139. go func(device models.Device) {
  140. err := scene.GlobalManager().UpdateDevice(context.Background(), &device, nil)
  141. if err != nil {
  142. log.Println("Failed to update devices' scene in resposne to event:", err)
  143. }
  144. }(device)
  145. }
  146. }
  147. for _, handler := range deadHandlers {
  148. wg.Add(1)
  149. go func(handler models.EventHandler) {
  150. err := config.EventHandlerRepository().Delete(context.Background(), &handler)
  151. if err != nil {
  152. log.Println("Failed to delete spent one-shot event handler:", err)
  153. }
  154. wg.Done()
  155. }(handler)
  156. }
  157. wg.Wait()
  158. return
  159. }
  160. func handleSpecial(event models.Event) error {
  161. switch event.Name {
  162. case models.ENBridgeConnected:
  163. bridgeId, _ := strconv.Atoi(event.Payload["bridgeId"])
  164. bridge, err := config.BridgeRepository().Find(ctx, bridgeId)
  165. if err != nil {
  166. return err
  167. }
  168. devices, err := config.DeviceRepository().FetchByReference(ctx, models.RKBridgeID, event.Payload["bridgeId"])
  169. if err != nil {
  170. return err
  171. }
  172. driver, err := config.DriverProvider().Provide(bridge.Driver)
  173. if err != nil {
  174. return err
  175. }
  176. return driver.Publish(ctx, bridge, devices)
  177. default:
  178. return nil
  179. }
  180. }