basic HTTP api and uistate.
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
.env
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package lucifer3
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceKey struct{}
|
type ServiceKey struct{}
|
||||||
@@ -65,20 +63,7 @@ func (b *EventBus) JoinPrivileged(service ActiveService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *EventBus) RunCommand(command Command) {
|
func (b *EventBus) RunCommand(command Command) {
|
||||||
if cd := command.CommandDescription(); !strings.HasPrefix(cd, "SetState") {
|
fmt.Println("[COMMAND]", command.CommandDescription())
|
||||||
if setStates := atomic.LoadInt32(&b.setStates); setStates > 0 {
|
|
||||||
fmt.Println("[INFO]", setStates, "SetStates commands hidden.")
|
|
||||||
atomic.AddInt32(&b.setStates, -setStates)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("[COMMAND]", cd)
|
|
||||||
} else {
|
|
||||||
if atomic.AddInt32(&b.setStates, 1) >= 1000 {
|
|
||||||
fmt.Println("[INFO] 1000 SetStates commands hidden.")
|
|
||||||
atomic.AddInt32(&b.setStates, -1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.send(serviceMessage{command: command})
|
b.send(serviceMessage{command: command})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||||
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
|
"git.aiterp.net/lucifer3/server/services"
|
||||||
|
"git.aiterp.net/lucifer3/server/services/httpapiv1"
|
||||||
|
"git.aiterp.net/lucifer3/server/services/hue"
|
||||||
|
"git.aiterp.net/lucifer3/server/services/mysqldb"
|
||||||
|
"git.aiterp.net/lucifer3/server/services/nanoleaf"
|
||||||
|
"git.aiterp.net/lucifer3/server/services/uistate"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
bus := lucifer3.EventBus{}
|
||||||
|
|
||||||
|
resolver := services.NewResolver()
|
||||||
|
sceneMap := services.NewSceneMap(resolver)
|
||||||
|
|
||||||
|
database, err := mysqldb.Connect(
|
||||||
|
env("LUCIFER4_DB_HOST"),
|
||||||
|
envInt("LUCIFER4_DB_PORT"),
|
||||||
|
env("LUCIFER4_DB_USER"),
|
||||||
|
env("LUCIFER4_DB_PASSWORD"),
|
||||||
|
env("LUCIFER4_DB_SCHEMA"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Database failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpAPI, err := httpapiv1.New(env("LUCIFER4_HTTP_LISTEN"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("HTTP Listen failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.JoinPrivileged(resolver)
|
||||||
|
bus.JoinPrivileged(sceneMap)
|
||||||
|
bus.Join(services.NewEffectEnforcer(resolver, sceneMap))
|
||||||
|
bus.Join(nanoleaf.NewService())
|
||||||
|
bus.Join(hue.NewService())
|
||||||
|
bus.Join(uistate.NewService())
|
||||||
|
bus.Join(database)
|
||||||
|
bus.Join(httpAPI)
|
||||||
|
|
||||||
|
bus.RunEvent(events.Started{})
|
||||||
|
|
||||||
|
time.Sleep(time.Hour * 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
func env(key string) string {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
log.Fatalln("Expected env:", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func envInt(key string) int {
|
||||||
|
value, err := strconv.Atoi(os.Getenv(key))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Expected numeric env:", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
@@ -17,9 +17,16 @@ require (
|
|||||||
github.com/dustin/go-coap v0.0.0-20170214053734-ddcc80675fa4 // indirect
|
github.com/dustin/go-coap v0.0.0-20170214053734-ddcc80675fa4 // indirect
|
||||||
github.com/eriklupander/dtls v0.0.0-20190304211642-b36018226359 // indirect
|
github.com/eriklupander/dtls v0.0.0-20190304211642-b36018226359 // indirect
|
||||||
github.com/golang/protobuf v1.3.1 // indirect
|
github.com/golang/protobuf v1.3.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect
|
github.com/labstack/echo/v4 v4.10.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
github.com/labstack/gommon v0.4.0 // indirect
|
||||||
golang.org/x/text v0.3.0 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
golang.org/x/crypto v0.2.0 // indirect
|
||||||
|
golang.org/x/net v0.4.0 // indirect
|
||||||
|
golang.org/x/sys v0.3.0 // indirect
|
||||||
|
golang.org/x/text v0.5.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 // indirect
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 // indirect
|
||||||
google.golang.org/grpc v1.21.1 // indirect
|
google.golang.org/grpc v1.21.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,9 +33,19 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
|||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
|
||||||
|
github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ=
|
||||||
|
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||||
|
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -54,10 +64,20 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
||||||
|
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||||
|
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
@@ -65,10 +85,18 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|||||||
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
@@ -81,4 +109,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package color
|
package color
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/lucasb-eyer/go-colorful"
|
"github.com/lucasb-eyer/go-colorful"
|
||||||
"math"
|
"math"
|
||||||
@@ -15,6 +16,21 @@ type Color struct {
|
|||||||
XY *XY `json:"xy,omitempty"`
|
XY *XY `json:"xy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (col *Color) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(col.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (col *Color) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal(bytes, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*col, err = Parse(s)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (col *Color) IsHueSat() bool {
|
func (col *Color) IsHueSat() bool {
|
||||||
return col.HS != nil
|
return col.HS != nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package gentools
|
||||||
|
|
||||||
|
func ApplyUpdate[T any](dst *T, value *T) {
|
||||||
|
if value != nil {
|
||||||
|
*dst = *value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyUpdatePtr[T any](dst **T, value *T) {
|
||||||
|
if value != nil {
|
||||||
|
*dst = ShallowCopy(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyUpdateSlice[T any](dst *[]T, value []T) {
|
||||||
|
if value != nil {
|
||||||
|
*dst = append([]T{}, value...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyUpdateNonZero[T comparable](dst *T, value *T) {
|
||||||
|
var zero T
|
||||||
|
if value != nil && *value != zero {
|
||||||
|
*dst = *value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyUpdateNilZero[T comparable](dst **T, value *T) {
|
||||||
|
if value != nil {
|
||||||
|
var zero T
|
||||||
|
|
||||||
|
if *value == zero {
|
||||||
|
*dst = nil
|
||||||
|
} else {
|
||||||
|
valueCopy := *value
|
||||||
|
*dst = &valueCopy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyMapUpdate[K comparable, V any](dst *map[K]V, src map[K]*V) {
|
||||||
|
if *dst == nil {
|
||||||
|
dst = &map[K]V{}
|
||||||
|
}
|
||||||
|
for key, value := range src {
|
||||||
|
if value != nil {
|
||||||
|
(*dst)[key] = *value
|
||||||
|
} else {
|
||||||
|
delete(*dst, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyUpdateMapNilZero[K comparable, V comparable](dst *map[K]V, src map[K]V) {
|
||||||
|
var zero V
|
||||||
|
if *dst == nil {
|
||||||
|
dst = &map[K]V{}
|
||||||
|
}
|
||||||
|
for key, value := range src {
|
||||||
|
if value != zero {
|
||||||
|
(*dst)[key] = value
|
||||||
|
} else {
|
||||||
|
delete(*dst, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package httpapiv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||||
|
"git.aiterp.net/lucifer3/server/commands"
|
||||||
|
"git.aiterp.net/lucifer3/server/effects"
|
||||||
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
|
"git.aiterp.net/lucifer3/server/services/uistate"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(addr string) (lucifer3.Service, error) {
|
||||||
|
svc := &service{}
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
e.GET("/state", func(c echo.Context) error {
|
||||||
|
svc.mu.Lock()
|
||||||
|
data := svc.data
|
||||||
|
svc.mu.Unlock()
|
||||||
|
|
||||||
|
return c.JSON(200, data)
|
||||||
|
})
|
||||||
|
|
||||||
|
e.POST("/command", func(c echo.Context) error {
|
||||||
|
var input commandInput
|
||||||
|
err := c.Bind(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.mu.Lock()
|
||||||
|
bus := svc.bus
|
||||||
|
svc.mu.Unlock()
|
||||||
|
if bus == nil {
|
||||||
|
return c.String(413, "Waiting for bus")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case input.Assign != nil:
|
||||||
|
bus.RunCommand(commands.Assign{
|
||||||
|
ID: nil,
|
||||||
|
Match: input.Assign.Match,
|
||||||
|
Effect: input.Assign.Effect.Effect,
|
||||||
|
})
|
||||||
|
case input.PairDevice != nil:
|
||||||
|
bus.RunCommand(*input.PairDevice)
|
||||||
|
case input.ForgetDevice != nil:
|
||||||
|
bus.RunCommand(*input.ForgetDevice)
|
||||||
|
case input.SearchDevices != nil:
|
||||||
|
bus.RunCommand(*input.SearchDevices)
|
||||||
|
default:
|
||||||
|
return c.String(400, "No supported command found in input")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(200, input)
|
||||||
|
})
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
e.Listener = listener
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := e.Start(addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to listen to webserver")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
data uistate.Data
|
||||||
|
bus *lucifer3.EventBus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) Active() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
||||||
|
switch event := event.(type) {
|
||||||
|
case events.Started:
|
||||||
|
s.mu.Lock()
|
||||||
|
s.bus = bus
|
||||||
|
s.mu.Unlock()
|
||||||
|
case uistate.Patch:
|
||||||
|
s.mu.Lock()
|
||||||
|
s.data = s.data.WithPatch(event)
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
// TODO: Broadcast websockets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type commandInput struct {
|
||||||
|
Assign *assignInput `json:"assign,omitempty"`
|
||||||
|
PairDevice *commands.PairDevice `json:"pairDevice,omitempty"`
|
||||||
|
SearchDevices *commands.SearchDevices `json:"searchDevices,omitempty"`
|
||||||
|
ForgetDevice *commands.ForgetDevice `json:"forgetDevice,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type assignInput struct {
|
||||||
|
Match string `json:"match"`
|
||||||
|
Effect effects.Serializable `json:"effect"`
|
||||||
|
}
|
||||||
@@ -292,7 +292,7 @@ func (b *Bridge) makeCongruentLoop(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure this loop doesn't spam too hard
|
// Make sure this loop doesn't spam too hard
|
||||||
rateLimit := time.After(time.Second / 10)
|
rateLimit := time.After(time.Second / 15)
|
||||||
|
|
||||||
// Take states
|
// Take states
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ func (q *Queries) DeleteDeviceInfoLike(ctx context.Context, id string) error {
|
|||||||
const insertDeviceAlias = `-- name: InsertDeviceAlias :exec
|
const insertDeviceAlias = `-- name: InsertDeviceAlias :exec
|
||||||
INSERT INTO device_alias (id, alias)
|
INSERT INTO device_alias (id, alias)
|
||||||
VALUES (?, ?)
|
VALUES (?, ?)
|
||||||
|
ON DUPLICATE KEY UPDATE alias=alias
|
||||||
`
|
`
|
||||||
|
|
||||||
type InsertDeviceAliasParams struct {
|
type InsertDeviceAliasParams struct {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ FROM device_alias;
|
|||||||
|
|
||||||
-- name: InsertDeviceAlias :exec
|
-- name: InsertDeviceAlias :exec
|
||||||
INSERT INTO device_alias (id, alias)
|
INSERT INTO device_alias (id, alias)
|
||||||
VALUES (?, ?);
|
VALUES (?, ?)
|
||||||
|
ON DUPLICATE KEY UPDATE alias=alias;
|
||||||
|
|
||||||
-- name: ReplaceDeviceAuth :exec
|
-- name: ReplaceDeviceAuth :exec
|
||||||
REPLACE INTO device_auth (id, api_key, extras)
|
REPLACE INTO device_auth (id, api_key, extras)
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package uistate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/lucifer3/server/device"
|
||||||
|
"git.aiterp.net/lucifer3/server/effects"
|
||||||
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
|
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
Devices map[string]Device `json:"devices"`
|
||||||
|
Assignments map[uuid.UUID]Assignment `json:"assignments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Data) WithPatch(patches ...Patch) Data {
|
||||||
|
newData := d.Copy()
|
||||||
|
for _, patch := range patches {
|
||||||
|
if patch.Device != nil {
|
||||||
|
pd := d.ensureDevice(patch.Device.ID)
|
||||||
|
|
||||||
|
gentools.ApplyUpdateSlice(&pd.Aliases, patch.Device.SetAliases)
|
||||||
|
gentools.ApplyUpdate(&pd.Name, patch.Device.Name)
|
||||||
|
gentools.ApplyUpdatePtr(&pd.HWState, patch.Device.HWState)
|
||||||
|
gentools.ApplyUpdatePtr(&pd.HWMetadata, patch.Device.HWMetadata)
|
||||||
|
gentools.ApplyUpdatePtr(&pd.DesiredState, patch.Device.DesiredState)
|
||||||
|
gentools.ApplyUpdatePtr(&pd.Assignment, patch.Device.Assignment)
|
||||||
|
|
||||||
|
if patch.Device.AddAlias != nil {
|
||||||
|
pd.Aliases = append(pd.Aliases[:0:0], pd.Aliases...)
|
||||||
|
pd.Aliases = append(pd.Aliases, *patch.Device.AddAlias)
|
||||||
|
}
|
||||||
|
if patch.Device.RemoveAlias != nil {
|
||||||
|
for i, alias := range pd.Aliases {
|
||||||
|
if alias == *patch.Device.RemoveAlias {
|
||||||
|
pd.Aliases = append(pd.Aliases[:0:0], pd.Aliases...)
|
||||||
|
pd.Aliases = append(pd.Aliases[:i], pd.Aliases[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if patch.Device.ClearAssignment {
|
||||||
|
pd.Assignment = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Device.Delete {
|
||||||
|
delete(newData.Devices, pd.ID)
|
||||||
|
} else {
|
||||||
|
newData.Devices[pd.ID] = pd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Assignment != nil {
|
||||||
|
pa := d.ensureAssignment(patch.Assignment.ID)
|
||||||
|
gentools.ApplyUpdatePtr(&pa.Effect, patch.Assignment.Effect)
|
||||||
|
if patch.Assignment.AddDeviceID != nil {
|
||||||
|
pa.DeviceIDs = append(pa.DeviceIDs[:0:0], pa.DeviceIDs...)
|
||||||
|
pa.DeviceIDs = append(pa.DeviceIDs, *patch.Assignment.AddDeviceID)
|
||||||
|
}
|
||||||
|
if patch.Assignment.RemoveDeviceID != nil {
|
||||||
|
for i, id := range pa.DeviceIDs {
|
||||||
|
if id == *patch.Assignment.RemoveDeviceID {
|
||||||
|
pa.DeviceIDs = append(pa.DeviceIDs[:0:0], pa.DeviceIDs...)
|
||||||
|
pa.DeviceIDs[i] = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Assignment.Delete {
|
||||||
|
delete(newData.Assignments, pa.ID)
|
||||||
|
} else {
|
||||||
|
newData.Assignments[pa.ID] = pa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Data) Copy() Data {
|
||||||
|
return Data{
|
||||||
|
Devices: gentools.CopyMap(d.Devices),
|
||||||
|
Assignments: gentools.CopyMap(d.Assignments),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Data) ensureDevice(id string) Device {
|
||||||
|
if device, ok := d.Devices[id]; ok {
|
||||||
|
return device
|
||||||
|
} else {
|
||||||
|
return Device{ID: id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Data) ensureAssignment(id uuid.UUID) Assignment {
|
||||||
|
if assignment, ok := d.Assignments[id]; ok {
|
||||||
|
return assignment
|
||||||
|
} else {
|
||||||
|
return Assignment{ID: id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
HWMetadata *events.HardwareMetadata `json:"hwMetadata"`
|
||||||
|
HWState *events.HardwareState `json:"hwState"`
|
||||||
|
DesiredState *device.State `json:"desiredState"`
|
||||||
|
Aliases []string `json:"aliases"`
|
||||||
|
Assignment *uuid.UUID `json:"assignment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Assignment struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
DeviceIDs []string `json:"deviceIds"`
|
||||||
|
Effect *effects.Serializable `json:"effect"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package uistate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/lucifer3/server/device"
|
||||||
|
"git.aiterp.net/lucifer3/server/effects"
|
||||||
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Patch struct {
|
||||||
|
Assignment *AssignmentPatch `json:"assignment,omitempty"`
|
||||||
|
Device *DevicePatch `json:"device,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Patch) EventDescription() string {
|
||||||
|
if e.Device != nil {
|
||||||
|
switch {
|
||||||
|
case e.Device.DesiredState != nil:
|
||||||
|
return fmt.Sprintf("uistate.Patch(device=%s, desired state)", e.Device.ID)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("uistate.Patch(device=%s)", e.Device.ID)
|
||||||
|
}
|
||||||
|
} else if e.Assignment != nil {
|
||||||
|
return fmt.Sprintf("uistate.Patch(assignment=%s)", e.Assignment.ID)
|
||||||
|
} else {
|
||||||
|
return "uistate.Patch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DevicePatch struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
HWMetadata *events.HardwareMetadata `json:"hwMetadata,omitempty"`
|
||||||
|
HWState *events.HardwareState `json:"hwState,omitempty"`
|
||||||
|
DesiredState *device.State `json:"desiredState,omitempty"`
|
||||||
|
SetAliases []string `json:"setAliases,omitempty"`
|
||||||
|
AddAlias *string `json:"addAlias,omitempty"`
|
||||||
|
RemoveAlias *string `json:"removeAlias,omitempty"`
|
||||||
|
Assignment *uuid.UUID `json:"assignment,omitempty"`
|
||||||
|
ClearAssignment bool `json:"clearAssignment,omitempty"`
|
||||||
|
Delete bool `json:"delete,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssignmentPatch struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
AddDeviceID *string `json:"addDeviceId"`
|
||||||
|
RemoveDeviceID *string `json:"removeDeviceId"`
|
||||||
|
Effect *effects.Serializable `json:"effect"`
|
||||||
|
Delete bool `json:"delete"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package uistate
|
||||||
|
|
||||||
|
import (
|
||||||
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||||
|
"git.aiterp.net/lucifer3/server/commands"
|
||||||
|
"git.aiterp.net/lucifer3/server/effects"
|
||||||
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
|
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewService() lucifer3.ActiveService {
|
||||||
|
return &service{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
data Data
|
||||||
|
listener []chan Patch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) Active() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
|
||||||
|
var patches []Patch
|
||||||
|
|
||||||
|
switch command := command.(type) {
|
||||||
|
case commands.SetState:
|
||||||
|
patches = []Patch{{Device: &DevicePatch{ID: command.ID, DesiredState: &command.State}}}
|
||||||
|
case commands.SetStateBatch:
|
||||||
|
for id, state := range command {
|
||||||
|
patches = []Patch{{Device: &DevicePatch{ID: id, DesiredState: gentools.ShallowCopy(&state)}}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(patches) > 0 {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.data = s.data.WithPatch(patches...)
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
for _, patch := range patches {
|
||||||
|
bus.RunEvent(patch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
||||||
|
var patches []Patch
|
||||||
|
|
||||||
|
switch event := event.(type) {
|
||||||
|
case events.AliasAdded:
|
||||||
|
patches = []Patch{{Device: &DevicePatch{ID: event.ID, AddAlias: &event.Alias}}}
|
||||||
|
case events.AliasRemoved:
|
||||||
|
patches = []Patch{{Device: &DevicePatch{ID: event.ID, RemoveAlias: &event.Alias}}}
|
||||||
|
case events.HardwareState:
|
||||||
|
patches = []Patch{{Device: &DevicePatch{ID: event.ID, HWState: &event}}}
|
||||||
|
case events.HardwareMetadata:
|
||||||
|
patches = []Patch{{Device: &DevicePatch{ID: event.ID, HWMetadata: &event}}}
|
||||||
|
case events.AssignmentCreated:
|
||||||
|
patches = []Patch{{Assignment: &AssignmentPatch{
|
||||||
|
ID: event.ID,
|
||||||
|
Effect: &effects.Serializable{Effect: event.Effect},
|
||||||
|
}}}
|
||||||
|
case events.AssignmentRemoved:
|
||||||
|
patches = []Patch{{Assignment: &AssignmentPatch{
|
||||||
|
ID: event.ID,
|
||||||
|
Delete: true,
|
||||||
|
}}}
|
||||||
|
case events.DeviceAssigned:
|
||||||
|
// Un-assign from current assignment (if any)
|
||||||
|
if d, ok := s.data.Devices[event.DeviceID]; ok && d.Assignment != nil {
|
||||||
|
patches = append(patches, Patch{Assignment: &AssignmentPatch{
|
||||||
|
ID: *d.Assignment,
|
||||||
|
RemoveDeviceID: &d.ID,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to current assignment (if it's not cleared)
|
||||||
|
if event.AssignmentID != nil {
|
||||||
|
patches = append(patches, Patch{Assignment: &AssignmentPatch{
|
||||||
|
ID: *event.AssignmentID,
|
||||||
|
AddDeviceID: &event.DeviceID,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always set the assignment
|
||||||
|
patches = append(patches, Patch{Device: &DevicePatch{
|
||||||
|
ID: event.DeviceID,
|
||||||
|
Assignment: gentools.ShallowCopy(event.AssignmentID),
|
||||||
|
ClearAssignment: event.AssignmentID == nil,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(patches) > 0 {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.data = s.data.WithPatch(patches...)
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
for _, patch := range patches {
|
||||||
|
bus.RunEvent(patch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user