moar stuff.

This commit is contained in:
2023-09-23 14:04:13 +02:00
parent c7d3a3200e
commit 2188d489ea
48 changed files with 1684 additions and 91 deletions
+19 -2
View File
@@ -21,6 +21,7 @@ func NewService(resolver device.Resolver) lucifer3.ActiveService {
supportFlags: make(map[string]device.SupportFlags),
colorFlags: make(map[string]device.ColorFlags),
ctRanges: make(map[string]*[2]int),
temperatures: make(map[string]float64),
motions: make(map[string]float64),
}
@@ -39,8 +40,9 @@ type effectEnforcer struct {
started uint32
list []*effectEnforcerRun
index map[string]*effectEnforcerRun
list []*effectEnforcerRun
index map[string]*effectEnforcerRun
ctRanges map[string]*[2]int
}
func (s *effectEnforcer) Active() bool {
@@ -53,6 +55,7 @@ func (s *effectEnforcer) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Even
s.mu.Lock()
colorFlags := s.colorFlags
supportFlags := s.supportFlags
ctRanges := s.ctRanges
s.mu.Unlock()
colorFlags = gentools.CopyMap(colorFlags)
@@ -60,9 +63,15 @@ func (s *effectEnforcer) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Even
colorFlags[event.ID] = event.ColorFlags
supportFlags[event.ID] = event.SupportFlags
if event.ColorKelvinRange != nil {
ctRanges = gentools.CopyMap(ctRanges)
ctRanges[event.ID] = event.ColorKelvinRange
}
s.mu.Lock()
s.colorFlags = colorFlags
s.supportFlags = supportFlags
s.ctRanges = ctRanges
s.mu.Unlock()
case events.DeviceReady:
@@ -237,6 +246,7 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) {
s.mu.Lock()
colorFlags := s.colorFlags
supportFlags := s.supportFlags
ctRanges := s.ctRanges
for i, run := range s.list {
if run.dead {
@@ -307,11 +317,18 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) {
state.Color = nil
} else if state.Color != nil {
cf := colorFlags[id]
invalid := (state.Color.K != nil && !cf.HasAll(device.CFlagKelvin)) ||
(state.Color.XY != nil && !cf.HasAll(device.CFlagXY)) ||
(state.Color.RGB != nil && !cf.HasAll(device.CFlagRGB)) ||
(state.Color.HS != nil && !cf.HasAll(device.CFlagHS))
if state.Color.K != nil && cf.IsColor() && ctRanges[id] != nil {
if *state.Color.K < ctRanges[id][0] || *state.Color.K >= ctRanges[id][1] {
invalid = true
}
}
if invalid {
var converted color.Color
var ok bool
+19
View File
@@ -5,6 +5,7 @@ import (
"git.aiterp.net/lucifer3/server/commands"
"git.aiterp.net/lucifer3/server/effects"
"git.aiterp.net/lucifer3/server/events"
"git.aiterp.net/lucifer3/server/internal/color"
"git.aiterp.net/lucifer3/server/services/script"
"git.aiterp.net/lucifer3/server/services/uistate"
"github.com/google/uuid"
@@ -38,6 +39,24 @@ func New(addr string) (lucifer3.Service, error) {
e.HidePort = true
e.Use(middleware.CORS())
e.GET("/color/:value", func(c echo.Context) error {
col, err := color.Parse(c.Param("value"))
if err != nil {
return c.String(400, err.Error())
}
rgb, _ := col.ToRGB()
xy, _ := col.ToXY()
hs, _ := col.ToHS()
return c.JSON(200, color.Color{
K: col.K,
RGB: rgb.RGB,
HS: hs.HS,
XY: xy.XY,
})
})
e.GET("/state", func(c echo.Context) error {
svc.mu.Lock()
data := svc.data
+34 -8
View File
@@ -18,6 +18,7 @@ import (
func NewBridge(host string, client *Client) *Bridge {
return &Bridge{
mu: sync.Mutex{},
client: client,
host: host,
ctx: context.Background(),
@@ -26,6 +27,7 @@ func NewBridge(host string, client *Client) *Bridge {
activeStates: map[string]device.State{},
desiredStates: map[string]device.State{},
colorFlags: map[string]device.ColorFlags{},
reachable: nil,
hasSeen: map[string]bool{},
triggerCongruenceCheckCh: make(chan struct{}, 2),
}
@@ -46,6 +48,7 @@ type Bridge struct {
reachable map[string]bool
hasSeen map[string]bool
lastMotion map[string]time.Time
lastPresence map[string]bool
lastButton map[string]time.Time
triggerCongruenceCheckCh chan struct{}
@@ -115,12 +118,14 @@ func (b *Bridge) RefreshAll() ([]lucifer3.Event, error) {
hasSeen := b.hasSeen
reachable := b.reachable
lastMotion := b.lastMotion
lastPresence := b.lastPresence
b.mu.Unlock()
oldHasSeen := hasSeen
hasSeen = gentools.CopyMap(hasSeen)
reachable = gentools.CopyMap(reachable)
lastMotion = gentools.CopyMap(lastMotion)
lastPresence = gentools.CopyMap(lastPresence)
colorFlags := make(map[string]device.ColorFlags)
activeStates := make(map[string]device.State)
@@ -144,13 +149,17 @@ func (b *Bridge) RefreshAll() ([]lucifer3.Event, error) {
})
lastMotion[b.fullId(*res)] = time.Now()
lastPresence[b.fullId(*res)] = true
} else {
extraEvents = append(extraEvents, events.MotionSensed{
ID: b.fullId(*res),
SecondsSince: 301,
})
if lastMotion[b.fullId(*res)].IsZero() {
extraEvents = append(extraEvents, events.MotionSensed{
ID: b.fullId(*res),
SecondsSince: 301,
})
lastMotion[b.fullId(*res)] = time.Now().Add(-time.Millisecond * 301)
lastMotion[b.fullId(*res)] = time.Now().Add(-time.Millisecond * 301)
}
lastPresence[b.fullId(*res)] = false
}
}
}
@@ -180,6 +189,7 @@ func (b *Bridge) RefreshAll() ([]lucifer3.Event, error) {
b.activeStates = activeStates
b.reachable = reachable
b.lastMotion = lastMotion
b.lastPresence = lastPresence
b.mu.Unlock()
return append(newEvents, extraEvents...), nil
@@ -193,6 +203,7 @@ func (b *Bridge) ApplyPatches(date time.Time, resources []ResourceData) (eventLi
colorFlags := b.colorFlags
lastMotion := b.lastMotion
lastButton := b.lastButton
lastPresence := b.lastPresence
b.mu.Unlock()
mapCopy := gentools.CopyMap(resourceMap)
@@ -201,6 +212,7 @@ func (b *Bridge) ApplyPatches(date time.Time, resources []ResourceData) (eventLi
colorFlagsCopy := gentools.CopyMap(colorFlags)
lastMotionCopy := gentools.CopyMap(lastMotion)
lastButtonCopy := gentools.CopyMap(lastButton)
lastPresenceCopy := gentools.CopyMap(lastPresence)
for _, resource := range resources {
if mapCopy[resource.ID] != nil {
@@ -218,6 +230,8 @@ func (b *Bridge) ApplyPatches(date time.Time, resources []ResourceData) (eventLi
}
for _, resource := range resources {
fullID := b.fullId(resource)
if resource.Owner != nil && resource.Owner.Kind == "device" {
if parent, ok := mapCopy[resource.Owner.ID]; ok && !strings.HasPrefix(parent.Metadata.Archetype, "bridge") {
hwState, _ := parent.GenerateEvent(b.host, mapCopy)
@@ -231,18 +245,24 @@ func (b *Bridge) ApplyPatches(date time.Time, resources []ResourceData) (eventLi
if resource.Temperature != nil {
eventList = append(eventList, events.TemperatureChanged{
ID: b.fullId(resource),
ID: fullID,
Temperature: resource.Temperature.Temperature,
})
}
if resource.Motion != nil {
lastMotionCopy[fullID] = time.Now()
if resource.Motion.Motion {
eventList = append(eventList, events.MotionSensed{
ID: b.fullId(resource),
ID: fullID,
SecondsSince: 0,
})
lastMotionCopy[b.fullId(resource)] = time.Now()
lastMotion[fullID] = time.Now()
lastPresenceCopy[fullID] = true
} else if lastPresenceCopy[fullID] {
lastMotion[fullID] = time.Now()
lastPresenceCopy[fullID] = false
}
}
if resource.Button != nil {
@@ -285,6 +305,7 @@ func (b *Bridge) ApplyPatches(date time.Time, resources []ResourceData) (eventLi
b.colorFlags = colorFlagsCopy
b.lastMotion = lastMotionCopy
b.lastButton = lastButtonCopy
b.lastPresence = lastPresenceCopy
b.mu.Unlock()
return
@@ -382,9 +403,14 @@ func (b *Bridge) Run(ctx context.Context, bus *lucifer3.EventBus) interface{} {
case <-quickStep.C:
b.mu.Lock()
lastMotion := b.lastMotion
lastPresence := b.lastPresence
b.mu.Unlock()
for id, value := range lastMotion {
if lastPresence[id] {
continue
}
since := time.Since(value)
sinceMod := since % (time.Second * 30)
if (since > time.Second*20) && (sinceMod >= time.Second*27 || sinceMod <= time.Second*3) {
@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE script_trigger ADD COLUMN name VARCHAR(255) NOT NULL DEFAULT '';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE script_trigger DROP COLUMN IF EXISTS name;
-- +goose StatementEnd
+1
View File
@@ -49,6 +49,7 @@ type ScriptTrigger struct {
ScriptName string
ScriptPre json.RawMessage
ScriptPost json.RawMessage
Name string
}
type ScriptVariable struct {
+6 -3
View File
@@ -22,7 +22,7 @@ func (q *Queries) DeleteScriptTrigger(ctx context.Context, id uuid.UUID) error {
}
const listScriptTriggers = `-- name: ListScriptTriggers :many
SELECT id, event, device_match, parameter, script_target, script_name, script_pre, script_post FROM script_trigger
SELECT id, event, device_match, parameter, script_target, script_name, script_pre, script_post, name FROM script_trigger
`
func (q *Queries) ListScriptTriggers(ctx context.Context) ([]ScriptTrigger, error) {
@@ -43,6 +43,7 @@ func (q *Queries) ListScriptTriggers(ctx context.Context) ([]ScriptTrigger, erro
&i.ScriptName,
&i.ScriptPre,
&i.ScriptPost,
&i.Name,
); err != nil {
return nil, err
}
@@ -112,12 +113,13 @@ func (q *Queries) ListScripts(ctx context.Context) ([]Script, error) {
}
const replaceScriptTrigger = `-- name: ReplaceScriptTrigger :exec
REPLACE INTO script_trigger (id, event, device_match, parameter, script_target, script_name, script_pre, script_post)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
REPLACE INTO script_trigger (id, name, event, device_match, parameter, script_target, script_name, script_pre, script_post)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`
type ReplaceScriptTriggerParams struct {
ID uuid.UUID
Name string
Event string
DeviceMatch string
Parameter string
@@ -130,6 +132,7 @@ type ReplaceScriptTriggerParams struct {
func (q *Queries) ReplaceScriptTrigger(ctx context.Context, arg ReplaceScriptTriggerParams) error {
_, err := q.exec(ctx, q.replaceScriptTriggerStmt, replaceScriptTrigger,
arg.ID,
arg.Name,
arg.Event,
arg.DeviceMatch,
arg.Parameter,
+2 -2
View File
@@ -14,8 +14,8 @@ REPLACE INTO script_variable (scope, name, value) VALUES (?, ?, ?);
SELECT * FROM script_trigger;
-- name: ReplaceScriptTrigger :exec
REPLACE INTO script_trigger (id, event, device_match, parameter, script_target, script_name, script_pre, script_post)
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
REPLACE INTO script_trigger (id, name, event, device_match, parameter, script_target, script_name, script_pre, script_post)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
-- name: DeleteScriptTrigger :exec
DELETE FROM script_trigger WHERE id = ?;
+3 -1
View File
@@ -34,7 +34,7 @@ func (d *database) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
switch event := event.(type) {
case events.Started:
timeout, cancel := context.WithTimeout(context.Background(), time.Second*10)
timeout, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
// Fetch all aliases first
@@ -185,6 +185,7 @@ func (d *database) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
for _, trig := range scriptTriggers {
bus.RunCommand(script.UpdateTrigger{
ID: trig.ID,
Name: trig.Name,
Event: script.TriggerEvent(trig.Event),
DeviceMatch: trig.DeviceMatch,
Parameter: trig.Parameter,
@@ -389,6 +390,7 @@ func (d *database) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Comman
ScriptName: command.ScriptName,
ScriptPre: toJSON(command.ScriptPre),
ScriptPost: toJSON(command.ScriptPost),
Name: command.Name,
})
if err != nil {
bus.RunEvent(events.Log{
+9 -9
View File
@@ -141,15 +141,15 @@ var shapeTypeMap = map[int]string{
}
var shapeIconMap = map[int]string{
0: "triangle",
1: "triangle",
2: "square",
3: "square",
4: "square",
7: "hexagon",
8: "triangle",
9: "triangle",
12: "hexagon",
0: "shape_triangle",
1: "shape_triangle",
2: "shape_square",
3: "shape_square",
4: "shape_square",
7: "shape_hexagon",
8: "shape_triangle",
9: "shape_triangle",
12: "shape_hexagon",
}
var shapeWidthMap = map[int]int{
+1
View File
@@ -20,6 +20,7 @@ const (
type Trigger struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Event TriggerEvent `json:"event"`
DeviceMatch string `json:"deviceMatch"`
Parameter string `json:"parameter"`
+9 -1
View File
@@ -53,7 +53,15 @@ func (d *Data) WithPatch(patches ...Patch) Data {
}
if patch.Device.HWMetadata != nil {
if pd.Icon == "" {
hasIconAlias := false
for _, a := range pd.Aliases {
if strings.HasPrefix(a, "lucifer:icon") {
hasIconAlias = true
break
}
}
if !hasIconAlias {
pd.Icon = patch.Device.HWMetadata.Icon
}
}