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.

152 lines
4.0 KiB

  1. package script
  2. import (
  3. "fmt"
  4. lucifer3 "git.aiterp.net/lucifer3/server"
  5. "git.aiterp.net/lucifer3/server/commands"
  6. "git.aiterp.net/lucifer3/server/device"
  7. "git.aiterp.net/lucifer3/server/internal/formattools"
  8. "git.aiterp.net/lucifer3/server/internal/gentools"
  9. "github.com/google/uuid"
  10. "sort"
  11. )
  12. type service struct {
  13. resolver device.Resolver
  14. variables *Variables
  15. scripts map[string]*Script
  16. triggers map[uuid.UUID]*Trigger
  17. }
  18. func (s *service) Active() bool {
  19. return true
  20. }
  21. func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
  22. for _, trig := range s.triggers {
  23. if trig.Check(event, s.resolver) {
  24. lines := append(make([]Line, 0, 64), trig.ScriptPre...)
  25. script := s.scripts[trig.ScriptName]
  26. if script != nil {
  27. lines = append(lines, script.Lines...)
  28. }
  29. lines = append(lines, trig.ScriptPost...)
  30. variables := s.variables.Get()
  31. devices := s.resolver.Resolve(trig.ScriptTarget)
  32. sort.Slice(devices, func(i, j int) bool {
  33. return devices[i].ID < devices[j].ID
  34. })
  35. fmt.Println("[TRIGGER] Running", trig.ID, "for", len(devices), "devices:", event.EventDescription())
  36. s.runScript(bus, lines, trig.ScriptTarget, map[string]bool{}, devices, variables)
  37. }
  38. }
  39. }
  40. func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  41. switch command := command.(type) {
  42. case Update:
  43. s.scripts[command.Name] = &Script{
  44. Name: command.Name,
  45. Lines: command.Lines,
  46. }
  47. case Execute:
  48. script := s.scripts[command.Name]
  49. if script == nil {
  50. break
  51. }
  52. variables := s.variables.Get()
  53. devices := s.resolver.Resolve(command.Match)
  54. sort.Slice(devices, func(i, j int) bool {
  55. return devices[i].ID < devices[j].ID
  56. })
  57. s.runScript(bus, script.Lines, command.Match, map[string]bool{}, devices, variables)
  58. case UpdateTrigger:
  59. s.triggers[command.ID] = gentools.Ptr(Trigger(command))
  60. case DeleteTrigger:
  61. delete(s.triggers, command.ID)
  62. }
  63. }
  64. func NewService(resolver device.Resolver, variables *Variables) lucifer3.Service {
  65. return &service{
  66. resolver: resolver,
  67. variables: variables,
  68. triggers: make(map[uuid.UUID]*Trigger, 8),
  69. scripts: make(map[string]*Script, 8),
  70. }
  71. }
  72. func (s *service) runScript(bus *lucifer3.EventBus, lines []Line, match string, matchedDevices map[string]bool, devices []device.Pointer, variables VariableSet) {
  73. for _, line := range lines {
  74. if line.If != nil {
  75. devicesIDs := gentools.Map(devices, func(d device.Pointer) string { return d.ID })
  76. if line.If.Condition.Check(variables, match, devicesIDs) {
  77. s.runScript(bus, line.If.Then, match, matchedDevices, devices, variables)
  78. } else {
  79. s.runScript(bus, line.If.Else, match, matchedDevices, devices, variables)
  80. }
  81. } else if line.Assign != nil {
  82. matcher := s.resolver.CompileMatcher(line.Assign.Match)
  83. matched := make([]string, 0)
  84. for _, dev := range devices {
  85. if matchedDevices[dev.ID] {
  86. continue
  87. }
  88. if line.Assign.Match == "*" || dev.Matches(matcher) {
  89. matchedDevices[dev.ID] = true
  90. matched = append(matched, dev.ID)
  91. }
  92. }
  93. if len(matched) > 0 {
  94. bus.RunCommand(commands.Assign{
  95. Match: formattools.CompactMatch(matched),
  96. Effect: line.Assign.Effect.Effect,
  97. })
  98. }
  99. } else if line.Set != nil {
  100. switch line.Set.Scope {
  101. case "devices":
  102. bus.RunCommand(SetVariable{
  103. Devices: gentools.Map(devices, func(d device.Pointer) string {
  104. return d.ID
  105. }),
  106. Key: line.Set.Key,
  107. Value: line.Set.Value,
  108. })
  109. case "match":
  110. bus.RunCommand(SetVariable{
  111. Match: gentools.Ptr(match),
  112. Key: line.Set.Key,
  113. Value: line.Set.Value,
  114. })
  115. case "global":
  116. bus.RunCommand(SetVariable{
  117. Key: line.Set.Key,
  118. Value: line.Set.Value,
  119. })
  120. }
  121. } else if line.Select != nil {
  122. matcher := s.resolver.CompileMatcher(line.Select.Match)
  123. matched := make([]device.Pointer, 0)
  124. for _, dev := range devices {
  125. if dev.Matches(matcher) {
  126. matched = append(matched, dev)
  127. }
  128. }
  129. if len(matched) > 0 {
  130. s.runScript(bus, line.Select.Then, match, matchedDevices, matched, variables)
  131. }
  132. }
  133. }
  134. }