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.

249 lines
6.1 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 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/publisher"
  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. if !handler.IsWithinTimeRange() {
  82. continue
  83. }
  84. devices, err := config.DeviceRepository().FetchByReference(ctx, handler.TargetKind, handler.TargetValue)
  85. if err != nil {
  86. log.Printf("Error fetchin' event devices: %s", err)
  87. return
  88. }
  89. if !handler.MatchesEvent(event, devices) {
  90. continue
  91. }
  92. if handler.OneShot {
  93. deadHandlers = append(deadHandlers, handler)
  94. }
  95. if handler.Priority > highestPriority {
  96. highestPriority = handler.Priority
  97. prioritizedEvent = handler.Actions.FireEvent
  98. }
  99. for _, device := range devices {
  100. _, ok := actions[device.ID]
  101. if !ok {
  102. allDevices = append(allDevices, device)
  103. actions[device.ID] = handler.Actions
  104. priorities[device.ID] = handler.Priority
  105. } else if handler.Priority > priorities[device.ID] {
  106. actions[device.ID] = handler.Actions
  107. priorities[device.ID] = handler.Priority
  108. }
  109. }
  110. }
  111. if prioritizedEvent != nil {
  112. responses = append(responses, *prioritizedEvent)
  113. }
  114. for i, device := range allDevices {
  115. action := actions[device.ID]
  116. newState := models.NewDeviceState{}
  117. newState.Color = action.SetColor
  118. newState.Power = action.SetPower
  119. if action.SetIntensity != nil {
  120. newState.Intensity = action.SetIntensity
  121. } else if action.AddIntensity != nil {
  122. newIntensity := math.Max(0, math.Min(1, device.State.Intensity+*action.AddIntensity))
  123. newState.Intensity = &newIntensity
  124. }
  125. if action.SetTemperature != nil {
  126. newState.Temperature = action.SetTemperature
  127. }
  128. err = allDevices[i].SetState(newState)
  129. if err != nil {
  130. log.Println("Error updating state for device", device.ID, "err:", err)
  131. }
  132. if action.SetScene != nil {
  133. action.SetScene.StartTime = time.Now()
  134. allDevices[i].SceneAssignments = []models.DeviceSceneAssignment{*action.SetScene}
  135. }
  136. if action.PushScene != nil {
  137. action.PushScene.StartTime = time.Now()
  138. allDevices[i].SceneAssignments = append(allDevices[i].SceneAssignments, *action.PushScene)
  139. }
  140. }
  141. config.PublishChannel <- allDevices
  142. wg := sync.WaitGroup{}
  143. if len(allDevices) > 0 {
  144. wg.Add(1)
  145. go func() {
  146. err := config.DeviceRepository().SaveMany(context.Background(), models.SMState, allDevices)
  147. if err != nil {
  148. log.Println("Failed to save devices' state:", err)
  149. }
  150. wg.Done()
  151. }()
  152. }
  153. for _, handler := range deadHandlers {
  154. wg.Add(1)
  155. go func(handler models.EventHandler) {
  156. err := config.EventHandlerRepository().Delete(context.Background(), &handler)
  157. if err != nil {
  158. log.Println("Failed to delete spent one-shot event handler:", err)
  159. }
  160. wg.Done()
  161. }(handler)
  162. }
  163. wg.Wait()
  164. return
  165. }
  166. func handleSpecial(event models.Event) error {
  167. switch event.Name {
  168. case models.ENBridgeConnected:
  169. bridgeId, _ := strconv.Atoi(event.Payload["bridgeId"])
  170. bridge, err := config.BridgeRepository().Find(ctx, bridgeId)
  171. if err != nil {
  172. return err
  173. }
  174. devices, err := config.DeviceRepository().FetchByReference(ctx, models.RKBridgeID, event.Payload["bridgeId"])
  175. if err != nil {
  176. return err
  177. }
  178. driver, err := config.DriverProvider().Provide(bridge.Driver)
  179. if err != nil {
  180. return err
  181. }
  182. return driver.Publish(ctx, bridge, devices)
  183. case models.ENSensorPresenceStarted, models.ENSensorPresenceEnded, models.ENSensorTemperature:
  184. id, _ := strconv.Atoi(event.Payload["deviceId"])
  185. updateTime, _ := strconv.ParseInt(event.Payload["lastUpdated"], 10, 64)
  186. state := models.SceneSensor{
  187. ID: id,
  188. UpdateTime: time.Unix(updateTime, 0),
  189. }
  190. switch event.Name {
  191. case models.ENSensorPresenceStarted:
  192. presence := true
  193. state.Presence = &presence
  194. case models.ENSensorPresenceEnded:
  195. presence := false
  196. state.Presence = &presence
  197. case models.ENSensorTemperature:
  198. temperature, _ := strconv.ParseFloat(event.Payload["temperature"], 64)
  199. state.Temperature = &temperature
  200. }
  201. publisher.Global().UpdateSensor(state)
  202. return nil
  203. default:
  204. return nil
  205. }
  206. }