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.

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