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.
161 lines
4.2 KiB
161 lines
4.2 KiB
package script
|
|
|
|
import (
|
|
"fmt"
|
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
|
"git.aiterp.net/lucifer3/server/commands"
|
|
"git.aiterp.net/lucifer3/server/device"
|
|
"git.aiterp.net/lucifer3/server/internal/formattools"
|
|
"git.aiterp.net/lucifer3/server/internal/gentools"
|
|
"github.com/google/uuid"
|
|
"sort"
|
|
)
|
|
|
|
type service struct {
|
|
resolver device.Resolver
|
|
variables *Variables
|
|
scripts map[string]*Script
|
|
triggers map[uuid.UUID]*Trigger
|
|
}
|
|
|
|
func (s *service) Active() bool {
|
|
return true
|
|
}
|
|
|
|
func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
|
for _, trig := range s.triggers {
|
|
if trig.Check(event, s.resolver) {
|
|
lines := append(make([]Line, 0, 64), trig.ScriptPre...)
|
|
script := s.scripts[trig.ScriptName]
|
|
if script != nil {
|
|
lines = append(lines, script.Lines...)
|
|
}
|
|
lines = append(lines, trig.ScriptPost...)
|
|
|
|
variables := s.variables.Get()
|
|
devices := s.resolver.Resolve(trig.ScriptTarget)
|
|
sort.Slice(devices, func(i, j int) bool {
|
|
return devices[i].ID < devices[j].ID
|
|
})
|
|
|
|
fmt.Println("[TRIGGER] Running", trig.ID, "for", len(devices), "devices:", event.EventDescription())
|
|
|
|
s.runScript(bus, lines, trig.ScriptTarget, map[string]bool{}, devices, variables)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
|
|
switch command := command.(type) {
|
|
case Update:
|
|
s.scripts[command.Name] = &Script{
|
|
Name: command.Name,
|
|
Lines: command.Lines,
|
|
}
|
|
case Execute:
|
|
script := s.scripts[command.Name]
|
|
if script == nil {
|
|
break
|
|
}
|
|
|
|
variables := s.variables.Get()
|
|
|
|
devices := s.resolver.Resolve(command.Match)
|
|
sort.Slice(devices, func(i, j int) bool {
|
|
return devices[i].ID < devices[j].ID
|
|
})
|
|
|
|
s.runScript(bus, script.Lines, command.Match, map[string]bool{}, devices, variables)
|
|
case UpdateTrigger:
|
|
s.triggers[command.ID] = gentools.Ptr(Trigger(command))
|
|
case DeleteTrigger:
|
|
delete(s.triggers, command.ID)
|
|
}
|
|
}
|
|
|
|
func NewService(resolver device.Resolver, variables *Variables) lucifer3.Service {
|
|
return &service{
|
|
resolver: resolver,
|
|
variables: variables,
|
|
triggers: make(map[uuid.UUID]*Trigger, 8),
|
|
scripts: make(map[string]*Script, 8),
|
|
}
|
|
}
|
|
|
|
func (s *service) runScript(bus *lucifer3.EventBus, lines []Line, match string, matchedDevices map[string]bool, devices []device.Pointer, variables VariableSet) {
|
|
for _, line := range lines {
|
|
if line.If != nil {
|
|
devicesIDs := gentools.Map(devices, func(d device.Pointer) string { return d.ID })
|
|
if line.If.Condition.Check(variables, match, devicesIDs) {
|
|
s.runScript(bus, line.If.Then, match, matchedDevices, devices, variables)
|
|
} else {
|
|
s.runScript(bus, line.If.Else, match, matchedDevices, devices, variables)
|
|
}
|
|
} else if line.Assign != nil {
|
|
matcher := s.resolver.CompileMatcher(line.Assign.Match)
|
|
|
|
matched := make([]string, 0)
|
|
for _, dev := range devices {
|
|
if matchedDevices[dev.ID] {
|
|
continue
|
|
}
|
|
|
|
if line.Assign.Match == "*" || dev.Matches(matcher) {
|
|
matchedDevices[dev.ID] = true
|
|
matched = append(matched, dev.ID)
|
|
}
|
|
}
|
|
|
|
if len(matched) > 0 {
|
|
bus.RunCommand(commands.Assign{
|
|
Match: formattools.CompactMatch(matched),
|
|
Effect: line.Assign.Effect.Effect,
|
|
})
|
|
}
|
|
} else if line.Set != nil {
|
|
var sv *SetVariable
|
|
|
|
switch line.Set.Scope {
|
|
case "devices":
|
|
sv = &SetVariable{
|
|
Devices: gentools.Map(devices, func(d device.Pointer) string {
|
|
return d.ID
|
|
}),
|
|
Key: line.Set.Key,
|
|
Value: line.Set.Value,
|
|
}
|
|
bus.RunCommand(sv)
|
|
case "match":
|
|
sv = &SetVariable{
|
|
Match: gentools.Ptr(match),
|
|
Key: line.Set.Key,
|
|
Value: line.Set.Value,
|
|
}
|
|
case "global":
|
|
sv = &SetVariable{
|
|
Match: gentools.Ptr(match),
|
|
Key: line.Set.Key,
|
|
Value: line.Set.Value,
|
|
}
|
|
}
|
|
|
|
if sv != nil {
|
|
variables = variables.With(*sv)
|
|
bus.RunCommand(*sv)
|
|
}
|
|
} else if line.Select != nil {
|
|
matcher := s.resolver.CompileMatcher(line.Select.Match)
|
|
|
|
matched := make([]device.Pointer, 0)
|
|
for _, dev := range devices {
|
|
if dev.Matches(matcher) {
|
|
matched = append(matched, dev)
|
|
}
|
|
}
|
|
|
|
if len(matched) > 0 {
|
|
s.runScript(bus, line.Select.Then, match, matchedDevices, matched, variables)
|
|
}
|
|
}
|
|
}
|
|
}
|