add script triggers.

This commit is contained in:
2023-08-29 21:00:29 +02:00
parent 8776ed6df3
commit e8b77ae69c
16 changed files with 434 additions and 27 deletions
+26 -9
View File
@@ -2,9 +2,26 @@ package script
import (
"fmt"
"github.com/google/uuid"
"strings"
)
type UpdateTrigger Trigger
func (c UpdateTrigger) CommandDescription() string {
return fmt.Sprintf("script.UpdateTrigger(%s, %s(%#+v, %#+v))",
c.ID, c.Event, c.DeviceMatch, c.Parameter,
)
}
type DeleteTrigger struct {
ID uuid.UUID `json:"id"`
}
func (c DeleteTrigger) CommandDescription() string {
return fmt.Sprintf("script.DeleteTrigger(%s)", c.ID)
}
type SetVariable struct {
Match *string
Devices []string
@@ -15,28 +32,28 @@ type SetVariable struct {
func (c SetVariable) CommandDescription() string {
switch {
case c.Match != nil:
return fmt.Sprintf("SetVariable(tag(%s), %s, %s)", *c.Match, c.Key, c.Value)
return fmt.Sprintf("script.SetVariable(tag(%s), %s, %s)", *c.Match, c.Key, c.Value)
case c.Devices != nil:
return fmt.Sprintf("SetVariable(devices(%s), %s, %s)", strings.Join(c.Devices, ", "), c.Key, c.Value)
return fmt.Sprintf("script.SetVariable(devices(%s), %s, %s)", strings.Join(c.Devices, ", "), c.Key, c.Value)
default:
return fmt.Sprintf("SetVariable(global, %s, %s)", c.Key, c.Value)
return fmt.Sprintf("script.SetVariable(global, %s, %s)", c.Key, c.Value)
}
}
type UpdateScript struct {
type Update struct {
Name string `json:"name"`
Lines []Line `json:"lines"`
}
func (c UpdateScript) CommandDescription() string {
return fmt.Sprintf("UpdateScript(%s, [%d lines...])", c.Name, len(c.Lines))
func (c Update) CommandDescription() string {
return fmt.Sprintf("script.Update(%s, [%d lines...])", c.Name, len(c.Lines))
}
type ExecuteScript struct {
type Execute struct {
Name string `json:"name"`
Match string `json:"match"`
}
func (c ExecuteScript) CommandDescription() string {
return fmt.Sprintf("ExecuteScript(%s, %s)", c.Name, c.Match)
func (c Execute) CommandDescription() string {
return fmt.Sprintf("script.Execute(%s, %s)", c.Name, c.Match)
}
+30 -10
View File
@@ -6,36 +6,51 @@ import (
"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"
"sync"
)
type service struct {
mu sync.Mutex
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(_ *lucifer3.EventBus, _ lucifer3.Event) {}
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
})
s.runScript(bus, script.Lines, trig.ScriptTarget, map[string]bool{}, devices, variables)
}
}
}
func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
switch command := command.(type) {
case UpdateScript:
s.mu.Lock()
case Update:
s.scripts[command.Name] = &Script{
Name: command.Name,
Lines: command.Lines,
}
s.mu.Unlock()
case ExecuteScript:
s.mu.Lock()
case Execute:
script := s.scripts[command.Name]
s.mu.Unlock()
if script == nil {
break
}
@@ -48,6 +63,10 @@ func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command
})
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)
}
}
@@ -55,7 +74,8 @@ func NewService(resolver device.Resolver, variables *Variables) lucifer3.Service
return &service{
resolver: resolver,
variables: variables,
scripts: map[string]*Script{},
triggers: make(map[uuid.UUID]*Trigger, 8),
scripts: make(map[string]*Script, 8),
}
}
+70
View File
@@ -0,0 +1,70 @@
package script
import (
"fmt"
lucifer3 "git.aiterp.net/lucifer3/server"
"git.aiterp.net/lucifer3/server/device"
"git.aiterp.net/lucifer3/server/events"
"github.com/google/uuid"
"strconv"
)
type TriggerEvent string
const (
TEButton TriggerEvent = "Button"
TETime TriggerEvent = "Time"
TETemperatureAbove TriggerEvent = "TemperatureAbove"
TETemperatureBelow TriggerEvent = "TemperatureBelow"
)
type Trigger struct {
ID uuid.UUID `json:"id"`
Event TriggerEvent `json:"event"`
DeviceMatch string `json:"deviceMatch"`
Parameter string `json:"parameter"`
ScriptTarget string `json:"scriptTarget"`
ScriptName string `json:"scriptName"`
ScriptPre []Line `json:"scriptPre"`
ScriptPost []Line `json:"scriptPost"`
}
func (t *Trigger) Check(event lucifer3.Event, res device.Resolver) bool {
devices := res.Resolve(t.DeviceMatch)
switch event := event.(type) {
case events.ButtonPressed:
if t.Event != TEButton {
return false
}
for _, dev := range devices {
if dev.ID == event.ID {
return t.Parameter == event.Name
}
}
case events.Time:
if t.Event != TETime {
return false
}
return fmt.Sprintf("%02d:%02d", event.Hour, event.Minute) == t.Parameter
case events.TemperatureChanged:
if t.Event != TETemperatureAbove && t.Event != TETemperatureBelow {
return false
}
num, err := strconv.ParseFloat(t.Parameter, 64)
if err != nil {
return false
}
for _, dev := range devices {
if dev.ID == event.ID {
return (event.Temperature >= num) == (t.Event == TETemperatureAbove)
}
}
}
return false
}