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.

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