add resolve service prototype.
This commit is contained in:
+16
-16
@@ -1,35 +1,30 @@
|
||||
package main
|
||||
|
||||
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/events"
|
||||
"git.aiterp.net/lucifer3/server/internal/color"
|
||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||
"log"
|
||||
"git.aiterp.net/lucifer3/server/services"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bus := lucifer3.EventBus{}
|
||||
|
||||
bus.JoinCallback(func(event lucifer3.Event) bool {
|
||||
switch event := event.(type) {
|
||||
case events.Connected:
|
||||
log.Println("Callback got connect event for", event.Prefix)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
bus.Join(services.Resolver())
|
||||
|
||||
bus.RunEvent(events.Connected{Prefix: "nanoleaf:10.80.1.11"})
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
for _, id := range []string{"e28c", "67db", "f744", "d057", "73c1"} {
|
||||
for i, id := range []string{"e28c", "67db", "f744", "d057", "73c1"} {
|
||||
bus.RunEvent(events.HardwareState{
|
||||
DeviceID: "nanoleaf:10.80.1.11:" + id,
|
||||
ID: "nanoleaf:10.80.1.11:" + id,
|
||||
InternalName: fmt.Sprintf("Hexagon %d", i+1),
|
||||
SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity,
|
||||
ColorFlags: device.CFlagRGB,
|
||||
State: device.State{},
|
||||
@@ -37,9 +32,12 @@ func main() {
|
||||
}
|
||||
|
||||
bus.RunCommand(commands.ReplaceScene{
|
||||
IDs: []string{"nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"},
|
||||
IDs: []string{"lucifer:name:Hexagon*"},
|
||||
SceneID: 7,
|
||||
})
|
||||
|
||||
time.Sleep(time.Second / 4)
|
||||
|
||||
for _, id := range []string{"nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"} {
|
||||
bus.RunCommand(commands.SetState{
|
||||
ID: id,
|
||||
@@ -54,15 +52,19 @@ func main() {
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
bus.RunEvent(events.HardwareState{
|
||||
DeviceID: "nanoleaf:10.80.1.11:40e5",
|
||||
ID: "nanoleaf:10.80.1.11:40e5",
|
||||
InternalName: "Hexagon 6",
|
||||
SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity,
|
||||
ColorFlags: device.CFlagRGB,
|
||||
State: device.State{},
|
||||
})
|
||||
bus.RunCommand(commands.ReplaceScene{
|
||||
IDs: []string{"nanoleaf:10.80.1.11:40e5", "nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"},
|
||||
IDs: []string{"lucifer:name:Hexagon*"},
|
||||
SceneID: 7,
|
||||
})
|
||||
|
||||
time.Sleep(time.Second / 4)
|
||||
|
||||
bus.RunCommand(commands.SetState{
|
||||
ID: "nanoleaf:10.80.1.11:40e5",
|
||||
State: device.State{
|
||||
@@ -73,6 +75,4 @@ func main() {
|
||||
})
|
||||
|
||||
time.Sleep(time.Second / 4)
|
||||
|
||||
time.Sleep(time.Second * 4)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.aiterp.net/lucifer3/server/internal/formattools"
|
||||
)
|
||||
|
||||
type SetName struct {
|
||||
ID string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c SetName) CommandDescription() string {
|
||||
return fmt.Sprintf("SetName(%v, %s)", c.ID, c.Name)
|
||||
}
|
||||
|
||||
type AddTag struct {
|
||||
IDs []string
|
||||
Tag string
|
||||
}
|
||||
|
||||
func (c AddTag) CommandDescription() string {
|
||||
return fmt.Sprintf("AddTag(%v, %s)", formattools.CompactIDList(c.IDs), c.Tag)
|
||||
}
|
||||
|
||||
type RemoveTag struct {
|
||||
IDs []string
|
||||
Tag string
|
||||
}
|
||||
|
||||
func (c RemoveTag) CommandDescription() string {
|
||||
return fmt.Sprintf("RemoveTag(%v, %s)", formattools.CompactIDList(c.IDs), c.Tag)
|
||||
}
|
||||
+2
-2
@@ -58,11 +58,11 @@ func (f ColorFlag) IsWarmWhite() bool {
|
||||
}
|
||||
|
||||
func (f ColorFlag) IsColorOnly() bool {
|
||||
return f != 0 && f&CFlagKelvin == 0
|
||||
return f.IsColor() && !f.IsWarmWhite()
|
||||
}
|
||||
|
||||
func (f ColorFlag) IsWarmWhiteOnly() bool {
|
||||
return f == CFlagKelvin
|
||||
return f.IsWarmWhite() && !f.IsColor()
|
||||
}
|
||||
|
||||
func (f ColorFlag) HasAny(d ColorFlag) bool {
|
||||
|
||||
@@ -11,11 +11,6 @@ type State struct {
|
||||
Temperature *float64 `json:"temperature"`
|
||||
Intensity *float64 `json:"intensity"`
|
||||
Color *color.Color `json:"color"`
|
||||
|
||||
SensedTemperature *float64 `json:"sensedTemperature"`
|
||||
SensedLightLevel *float64 `json:"sensedLightLevel"`
|
||||
SensedPresence *PresenceState `json:"sensedPresence"`
|
||||
SensedButton *int `json:"sensedButton"`
|
||||
}
|
||||
|
||||
func (d State) String() string {
|
||||
@@ -33,19 +28,6 @@ func (d State) String() string {
|
||||
parts = append(parts, fmt.Sprintf("color:%s", d.Color.String()))
|
||||
}
|
||||
|
||||
if d.SensedTemperature != nil {
|
||||
parts = append(parts, fmt.Sprintf("sensedTemperature:%.2f", *d.SensedTemperature))
|
||||
}
|
||||
if d.SensedLightLevel != nil {
|
||||
parts = append(parts, fmt.Sprintf("sensedLightLevel:%.1f", *d.SensedLightLevel))
|
||||
}
|
||||
if d.SensedPresence != nil {
|
||||
parts = append(parts, fmt.Sprintf("sensedPresense:(%t,%.1f)", d.SensedPresence.Present, d.SensedPresence.AbsenceSeconds))
|
||||
}
|
||||
if d.SensedButton != nil {
|
||||
parts = append(parts, fmt.Sprintf("sensedButton:%d", *d.SensedButton))
|
||||
}
|
||||
|
||||
return fmt.Sprint("(", strings.Join(parts, ", "), ")")
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -6,15 +6,17 @@ import (
|
||||
)
|
||||
|
||||
type HardwareState struct {
|
||||
DeviceID string `json:"internalId"`
|
||||
ID string `json:"internalId"`
|
||||
InternalName string `json:"internalName"`
|
||||
SupportFlags device.SupportFlags `json:"deviceFlags"`
|
||||
ColorFlags device.ColorFlag `json:"colorFlags"`
|
||||
Buttons []string `json:"buttons"`
|
||||
State device.State `json:"state"`
|
||||
}
|
||||
|
||||
func (d HardwareState) EventDescription() string {
|
||||
return fmt.Sprintf("HardwareState(id:%s, sflags:%s, cflags:%s, state:%s)",
|
||||
d.DeviceID, d.SupportFlags, d.ColorFlags, d.State,
|
||||
return fmt.Sprintf("HardwareState(id:%s, iname:%#+v, sflags:%s, cflags:%s, buttons:%v, state:%s)",
|
||||
d.ID, d.InternalName, d.SupportFlags, d.ColorFlags, d.Buttons, d.State,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package gentools
|
||||
|
||||
func IndexOf[T comparable](arr []T, value T) int {
|
||||
for i, v := range arr {
|
||||
if v == value {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func AddUniques[T comparable](arr *[]T, values ...T) {
|
||||
Outer:
|
||||
for _, v := range values {
|
||||
for _, v2 := range *arr {
|
||||
if v2 == v {
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
|
||||
*arr = append(*arr, v)
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ type Service interface {
|
||||
}
|
||||
|
||||
type ActiveService interface {
|
||||
Service
|
||||
|
||||
HandleCommand(bus *EventBus, command Command)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package services is for self-contained general services. Things like bridges, databases and such should
|
||||
// not be in here.
|
||||
package services
|
||||
@@ -0,0 +1,149 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||
"git.aiterp.net/lucifer3/server/commands"
|
||||
"git.aiterp.net/lucifer3/server/events"
|
||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Resolver() lucifer3.Service {
|
||||
return &resolver{
|
||||
tags: make(map[string][]string),
|
||||
names: make(map[string][]string),
|
||||
}
|
||||
}
|
||||
|
||||
type resolver struct {
|
||||
tags map[string][]string
|
||||
names map[string][]string
|
||||
}
|
||||
|
||||
func (r *resolver) Active() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *resolver) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) {
|
||||
switch event := event.(type) {
|
||||
case events.HardwareState:
|
||||
// On HardwareState, use the internal name if (and only if) it is not already named.
|
||||
|
||||
if event.InternalName != "" {
|
||||
found := false
|
||||
for _, ids := range r.names {
|
||||
i := gentools.IndexOf(ids, event.ID)
|
||||
if i != -1 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
r.names[event.InternalName] = append(r.names[event.InternalName], event.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
|
||||
var resolveList []string
|
||||
var resolveCB func(newIDs []string) lucifer3.Command
|
||||
|
||||
switch command := command.(type) {
|
||||
case commands.Assign:
|
||||
resolveList = command.IDs
|
||||
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
|
||||
case commands.ReplaceScene:
|
||||
resolveList = command.IDs
|
||||
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
|
||||
case commands.ClearScene:
|
||||
resolveList = command.IDs
|
||||
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
|
||||
case commands.SetName:
|
||||
if !strings.HasPrefix(command.ID, "lucifer:") {
|
||||
for name := range r.names {
|
||||
i := gentools.IndexOf(r.names[name], command.ID)
|
||||
if i != -1 {
|
||||
r.names[name] = append(r.names[name][:i], r.names[name][i+1:]...)
|
||||
}
|
||||
}
|
||||
r.names[command.Name] = append(r.names[command.Name], command.ID)
|
||||
}
|
||||
case commands.AddTag:
|
||||
for _, id := range command.IDs {
|
||||
if strings.HasPrefix(id, "lucifer:") {
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, id2 := range r.tags[command.Tag] {
|
||||
if id == id2 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
r.tags[command.Tag] = append(r.tags[command.Tag], id)
|
||||
}
|
||||
}
|
||||
|
||||
resolveList = command.IDs
|
||||
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
|
||||
case commands.RemoveTag:
|
||||
for _, id := range command.IDs {
|
||||
if strings.HasPrefix(id, "lucifer:") {
|
||||
continue
|
||||
}
|
||||
|
||||
for i, id2 := range r.tags[command.Tag] {
|
||||
if id == id2 {
|
||||
r.tags[command.Tag] = append(r.tags[command.Tag][:i], r.tags[command.Tag][i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolveList = command.IDs
|
||||
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
|
||||
}
|
||||
|
||||
if resolveList != nil && resolveCB != nil {
|
||||
newList := make([]string, 0, 16)
|
||||
|
||||
for _, id := range resolveList {
|
||||
switch {
|
||||
case strings.HasPrefix(id, "lucifer:tag:"):
|
||||
tag := id[12:]
|
||||
if len(newList) == 0 {
|
||||
newList = append(newList, r.tags[tag]...)
|
||||
} else {
|
||||
gentools.AddUniques(&newList, r.tags[tag]...)
|
||||
}
|
||||
case strings.HasPrefix(id, "lucifer:name:"):
|
||||
name := id[13:]
|
||||
if strings.HasSuffix(name, "*") {
|
||||
prefix := name[:len(name)-1]
|
||||
for name, ids := range r.names {
|
||||
if strings.HasPrefix(name, prefix) {
|
||||
gentools.AddUniques(&newList, ids...)
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(name, "*") {
|
||||
suffix := name[1:]
|
||||
for name, ids := range r.names {
|
||||
if strings.HasSuffix(name, suffix) {
|
||||
gentools.AddUniques(&newList, ids...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gentools.AddUniques(&newList, r.names[name]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(newList) > 0 {
|
||||
bus.RunCommand(resolveCB(newList))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user