add script triggers.
This commit is contained in:
@@ -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
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user