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.

281 lines
7.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
  1. package models
  2. import (
  3. "context"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. "time"
  8. )
  9. type EventHandler struct {
  10. ID int `json:"id"`
  11. EventName string `json:"eventName"`
  12. Conditions map[string]EventCondition `json:"conditions"`
  13. OneShot bool `json:"oneShot"`
  14. Priority int `json:"priority"`
  15. TargetKind ReferenceKind `json:"targetKind"`
  16. TargetValue string `json:"targetValue"`
  17. Actions EventAction `json:"actions"`
  18. From TimeOfDay `json:"from"`
  19. To TimeOfDay `json:"to"`
  20. }
  21. type EventHandlerRepository interface {
  22. FindByID(ctx context.Context, id int) (*EventHandler, error)
  23. FetchAll(ctx context.Context) ([]EventHandler, error)
  24. Save(ctx context.Context, handler *EventHandler) error
  25. Delete(ctx context.Context, handler *EventHandler) error
  26. }
  27. type EventCondition struct {
  28. EQ string `json:"eq,omitempty"`
  29. GT string `json:"gt,omitempty"`
  30. GTE string `json:"gte,omitempty"`
  31. LT string `json:"lt,omitempty"`
  32. LTE string `json:"lte,omitempty"`
  33. }
  34. type EventHandlerUpdate struct {
  35. SetEventName *string `json:"setEventName"`
  36. SetPriority *int `json:"setPriority"`
  37. SetTargetKind *ReferenceKind `json:"setTargetKind"`
  38. SetTargetValue *string `json:"setTargetValue"`
  39. SetOneShot *bool `json:"setOneShot"`
  40. SetConditions map[string]EventCondition `json:"setConditions"`
  41. PatchConditions map[string]*EventCondition `json:"patchConditions"`
  42. SetActions *EventAction `json:"setActions"`
  43. SetFrom *TimeOfDay `json:"setFrom"`
  44. SetTo *TimeOfDay `json:"setTo"`
  45. }
  46. func (h *EventHandler) ApplyUpdate(update EventHandlerUpdate) {
  47. if update.SetEventName != nil {
  48. h.EventName = *update.SetEventName
  49. }
  50. if update.SetPriority != nil {
  51. h.Priority = *update.SetPriority
  52. }
  53. if update.SetTargetKind != nil {
  54. h.TargetKind = *update.SetTargetKind
  55. }
  56. if update.SetTargetValue != nil {
  57. h.TargetValue = *update.SetTargetValue
  58. }
  59. if update.SetActions != nil {
  60. h.Actions = *update.SetActions
  61. }
  62. if update.SetOneShot != nil {
  63. h.OneShot = *update.SetOneShot
  64. }
  65. if update.SetFrom != nil {
  66. h.From = *update.SetFrom
  67. }
  68. if update.SetFrom != nil {
  69. h.To = *update.SetFrom
  70. }
  71. if update.SetConditions != nil {
  72. h.Conditions = update.SetConditions
  73. }
  74. if update.PatchConditions != nil {
  75. if h.Conditions == nil {
  76. h.Conditions = make(map[string]EventCondition)
  77. }
  78. for key, value := range update.PatchConditions {
  79. if value == nil {
  80. delete(h.Conditions, key)
  81. } else {
  82. h.Conditions[key] = *value
  83. }
  84. }
  85. }
  86. }
  87. func (h *EventHandler) MatchesEvent(event Event, targets []Device) bool {
  88. if event.Name != h.EventName {
  89. return false
  90. }
  91. for key, condition := range h.Conditions {
  92. if !strings.ContainsRune(key, '.') && !event.HasPayload(key) {
  93. return false
  94. }
  95. if !condition.check(key, event.Payload[key], targets) {
  96. return false
  97. }
  98. }
  99. return true
  100. }
  101. func (h *EventHandler) IsWithinTimeRange() bool {
  102. if h.From.IsNever() || h.To.IsNever() {
  103. return true
  104. }
  105. return CurrentTimeOfDay().IsBetween(h.From, h.To)
  106. }
  107. func (c *EventCondition) check(key, value string, targets []Device) bool {
  108. any := strings.HasPrefix(key, "any.")
  109. all := strings.HasPrefix(key, "all.")
  110. if any || all {
  111. count := 0
  112. total := 0
  113. for _, target := range targets {
  114. matches, skip := c.checkDevice(key[4:], target)
  115. if skip {
  116. continue
  117. }
  118. if matches {
  119. count++
  120. }
  121. total++
  122. }
  123. return (any && count > 0) || (all && count == total)
  124. }
  125. return c.matches(value)
  126. }
  127. func (c *EventCondition) checkDevice(key string, device Device) (matches bool, skip bool) {
  128. switch key {
  129. case "power":
  130. if !device.HasCapability(DCPower) {
  131. return false, true
  132. }
  133. return c.matches(strconv.FormatBool(device.State.Power)), false
  134. case "color":
  135. if !device.HasCapability(DCColorKelvin, DCColorHS, DCColorHSK) {
  136. return false, true
  137. }
  138. return c.matches(device.State.Color.String()), false
  139. case "intensity":
  140. if !device.HasCapability(DCIntensity) {
  141. return false, true
  142. }
  143. return c.matches(strconv.FormatFloat(device.State.Intensity, 'f', -1, 64)), false
  144. case "temperature":
  145. if !device.HasCapability(DCTemperatureControl, DCTemperatureSensor) {
  146. return false, true
  147. }
  148. return c.matches(strconv.Itoa(device.State.Temperature)), false
  149. case "scene":
  150. sceneId := -1
  151. for _, assignment := range device.SceneAssignments {
  152. duration := time.Duration(assignment.DurationMS) * time.Millisecond
  153. if duration <= 0 || time.Now().Before(assignment.StartTime.Add(duration)) {
  154. sceneId = assignment.SceneID
  155. }
  156. }
  157. return c.matches(strconv.Itoa(sceneId)), false
  158. default:
  159. return false, true
  160. }
  161. }
  162. var numRegex = regexp.MustCompile("^-*[0-9]+(.[0-9]+)*$")
  163. func (c *EventCondition) matches(value string) bool {
  164. if numRegex.MatchString(value) {
  165. numValue, _ := strconv.ParseFloat(value, 64)
  166. stillAlive := true
  167. if c.LT != "" {
  168. lt, _ := strconv.ParseFloat(c.LT, 64)
  169. stillAlive = numValue < lt
  170. }
  171. if stillAlive && c.LTE != "" {
  172. lte, _ := strconv.ParseFloat(c.LTE, 64)
  173. stillAlive = numValue <= lte
  174. }
  175. if stillAlive && c.EQ != "" {
  176. eq, _ := strconv.ParseFloat(c.EQ, 64)
  177. stillAlive = numValue == eq
  178. }
  179. if stillAlive && c.GTE != "" {
  180. gte, _ := strconv.ParseFloat(c.GTE, 64)
  181. stillAlive = numValue >= gte
  182. }
  183. if stillAlive && c.GT != "" {
  184. gt, _ := strconv.ParseFloat(c.GT, 64)
  185. stillAlive = numValue > gt
  186. }
  187. return stillAlive
  188. } else if c.EQ != "" {
  189. return strings.ToLower(c.EQ) == strings.ToLower(value)
  190. } else {
  191. return false
  192. }
  193. }
  194. type EventAction struct {
  195. SetPower *bool `json:"setPower"`
  196. SetColor *string `json:"setColor"`
  197. SetIntensity *float64 `json:"setIntensity"`
  198. SetTemperature *int `json:"setTemperature"`
  199. AddIntensity *float64 `json:"addIntensity"`
  200. FireEvent *Event `json:"fireEvent"`
  201. SetScene *DeviceSceneAssignment `json:"setScene"`
  202. PushScene *DeviceSceneAssignment `json:"pushScene"`
  203. }
  204. func (action *EventAction) Apply(other EventAction) {
  205. if action.SetPower == nil {
  206. action.SetPower = other.SetPower
  207. }
  208. if action.SetColor == nil {
  209. action.SetColor = other.SetColor
  210. }
  211. if action.SetIntensity == nil && other.SetIntensity != nil {
  212. action.SetIntensity = other.SetIntensity
  213. }
  214. if action.AddIntensity == nil && action.SetIntensity == nil {
  215. action.AddIntensity = other.AddIntensity
  216. }
  217. if action.FireEvent == nil {
  218. action.FireEvent = other.FireEvent
  219. }
  220. if action.SetScene == nil {
  221. action.SetScene = other.SetScene
  222. }
  223. if action.PushScene == nil {
  224. action.PushScene = other.PushScene
  225. }
  226. }
  227. func (c EventCondition) String() string {
  228. str := make([]string, 0, 5)
  229. if len(c.LT) > 0 {
  230. str = append(str, "lt:"+c.LT)
  231. }
  232. if len(c.LTE) > 0 {
  233. str = append(str, "lte:"+c.LTE)
  234. }
  235. if len(c.EQ) > 0 {
  236. str = append(str, c.EQ)
  237. }
  238. if len(c.GTE) > 0 {
  239. str = append(str, "gte:"+c.GTE)
  240. }
  241. if len(c.GT) > 0 {
  242. str = append(str, "gte:"+c.GT)
  243. }
  244. return strings.Join(str, ";")
  245. }