From 35c3a387c967f1e9375b6082320cae137e9b5696 Mon Sep 17 00:00:00 2001 From: Stian Fredrik Aune Date: Sat, 25 Sep 2021 17:26:27 +0200 Subject: [PATCH 1/4] lucy handler list --- app/client/handler.go | 16 ++++++++++++++++ cmd/lucy/handlercmd.go | 32 +++++++++++++++++++++++++++++++ cmd/lucy/help.go | 1 + cmd/lucy/main.go | 15 ++++++++++----- cmd/lucy/tables.go | 43 ++++++++++++++++++++++++++++++++++++++++++ go.sum | 6 ++++++ install-lucy.sh | 11 +++++++++++ models/eventhandler.go | 21 +++++++++++++++++++++ 8 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 app/client/handler.go create mode 100644 cmd/lucy/handlercmd.go create mode 100755 install-lucy.sh diff --git a/app/client/handler.go b/app/client/handler.go new file mode 100644 index 0000000..009fc39 --- /dev/null +++ b/app/client/handler.go @@ -0,0 +1,16 @@ +package client + +import ( + "context" + "git.aiterp.net/lucifer/new-server/models" +) + +func (client *Client) GetHandlers(ctx context.Context, fetchStr string) ([]models.EventHandler, error) { + handlers := make([]models.EventHandler, 0, 16) + err := client.Fetch(ctx, "GET", "/api/event-handlers", &handlers, nil) + if err != nil { + return nil, err + } + + return handlers, nil +} diff --git a/cmd/lucy/handlercmd.go b/cmd/lucy/handlercmd.go new file mode 100644 index 0000000..8410893 --- /dev/null +++ b/cmd/lucy/handlercmd.go @@ -0,0 +1,32 @@ +package main + +import ( + "context" + "fmt" + "git.aiterp.net/lucifer/new-server/app/client" + "log" + "os" +) + +func handlerCmd( + ctx context.Context, + c client.Client, +) { + cmd := parseCommand(os.Args[2:]) + + switch cmd.Name { + case "list": + handlers, err := c.GetHandlers(ctx, cmd.Params.Get(0).StringOr("all")) + if err != nil { + log.Fatalln(err) + } + + WriteHandlerInfoTable(os.Stdout, handlers) + default: + if cmd.Name != "help" { + log.Println("Unknown command:", cmd.Name) + } + + _, _ = fmt.Fprintln(os.Stderr, helpString[1:]) + } +} diff --git a/cmd/lucy/help.go b/cmd/lucy/help.go index 9206771..fc4a77b 100644 --- a/cmd/lucy/help.go +++ b/cmd/lucy/help.go @@ -8,6 +8,7 @@ EXAMPLES lucy run SetProfile name=evening DEVICE COMMANDS + handler list list set tag <[+/-]tag-name> diff --git a/cmd/lucy/main.go b/cmd/lucy/main.go index 0a1b601..fb2baa1 100644 --- a/cmd/lucy/main.go +++ b/cmd/lucy/main.go @@ -17,6 +17,8 @@ func main() { cfg = &Config{Remote: "http://localhost:8000"} } + ctx := context.Background() + c := client.Client{APIRoot: cfg.Remote} cmd := parseCommand(os.Args[1:]) @@ -30,7 +32,7 @@ func main() { fetchStr = &s } - devices, err := c.GetDevices(context.Background(), *fetchStr) + devices, err := c.GetDevices(ctx, *fetchStr) if err != nil { log.Fatalln(err) } @@ -39,7 +41,7 @@ func main() { } case "set": { - devices, err := c.PutDeviceState(context.Background(), cmd.Params.Get(0).StringOr("all"), models.NewDeviceState{ + devices, err := c.PutDeviceState(ctx, cmd.Params.Get(0).StringOr("all"), models.NewDeviceState{ Power: cmd.Params.Get("power").Bool(), Color: cmd.Params.Get("color").String(), Intensity: cmd.Params.Get("intensity").Float(), @@ -67,7 +69,7 @@ func main() { } devices, err := c.PutDeviceTags( - context.Background(), + ctx, cmd.Params.Get(0).StringOr("all"), addTags, removeTags, @@ -80,7 +82,7 @@ func main() { } case "update": { - devices, err := c.PutDevice(context.Background(), cmd.Params.Get(0).StringOr("all"), models.DeviceUpdate{ + devices, err := c.PutDevice(ctx, cmd.Params.Get(0).StringOr("all"), models.DeviceUpdate{ Name: cmd.Params.Get("name").String(), Icon: cmd.Params.Get("icon").String(), UserProperties: cmd.Params.Subset("prop.").StringPtrMap(), @@ -92,6 +94,9 @@ func main() { WriteDeviceInfoTable(os.Stdout, devices) } + case "handler": + handlerCmd(ctx, c) + // EVENT case "run": { @@ -105,7 +110,7 @@ func main() { Payload: cmd.Params.StringMap(), } - err := c.FireEvent(context.Background(), event) + err := c.FireEvent(ctx, event) if err != nil { log.Fatalln(err) } diff --git a/cmd/lucy/tables.go b/cmd/lucy/tables.go index 394eec0..8e639ab 100644 --- a/cmd/lucy/tables.go +++ b/cmd/lucy/tables.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "git.aiterp.net/lucifer/new-server/models" "github.com/olekukonko/tablewriter" "io" @@ -72,3 +73,45 @@ func WriteDeviceInfoTable(w io.Writer, devices []models.Device) { table.Render() } + +func WriteHandlerInfoTable(w io.Writer, handlers []models.EventHandler) { + table := tablewriter.NewWriter(w) + table.SetHeader([]string{"ID", "EVENT NAME", "ONE SHOT", "PRIORITY", "CONDITIONS", "TARGET", "ACTION"}) + table.SetReflowDuringAutoWrap(false) + + for _, h := range handlers { + condStr := "" + for key, value := range h.Conditions { + condStr += fmt.Sprintf("%s=%s ", key, value) + } + + actionStr := "" + if h.Actions.SetPower != nil { + actionStr += fmt.Sprintf("setPower=%t ", *h.Actions.SetPower) + } + if h.Actions.SetColor != nil { + actionStr += fmt.Sprintf("setColor=%s ", *h.Actions.SetColor) + } + if h.Actions.SetIntensity != nil { + actionStr += fmt.Sprintf("setIntensity=%.02f ", *h.Actions.SetIntensity) + } + if h.Actions.AddIntensity != nil { + actionStr += fmt.Sprintf("addIntensity=%.02f ", *h.Actions.AddIntensity) + } + if h.Actions.FireEvent != nil { + actionStr += fmt.Sprintf("fireEvent=%s ", (*h.Actions.FireEvent).Name) + } + + table.Append([]string{ + strconv.Itoa(h.ID), + h.EventName, + strconv.FormatBool(h.OneShot), + strconv.Itoa(h.Priority), + condStr, + fmt.Sprintf("%s:%s", h.TargetKind, h.TargetValue), + actionStr, + }) + } + + table.Render() +} diff --git a/go.sum b/go.sum index 7a4cc3e..18193e8 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,7 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w= github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= @@ -55,6 +56,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pressly/goose v2.7.0+incompatible h1:PWejVEv07LCerQEzMMeAtjuyCKbyprZ/LBa6K5P0OCQ= github.com/pressly/goose v2.7.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -69,6 +71,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 h1:3erb+vDS8lU1sxfDHF4/hhWyaXnhIaO+7RgL4fDZORA= golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -78,10 +81,13 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/install-lucy.sh b/install-lucy.sh new file mode 100755 index 0000000..8954ee0 --- /dev/null +++ b/install-lucy.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +printf "Building lucy...\n" +go mod download || exit 1 +go build -ldflags "-w -s" -o lucy ./cmd/lucy || exit 1 + +printf "Removing old lucy...\n" +sudo rm /usr/local/bin/lucy > /dev/null 2&>1 + +printf "Installing...\n" +sudo mv ./lucy /usr/local/bin/lucy diff --git a/models/eventhandler.go b/models/eventhandler.go index acbc292..840ff50 100644 --- a/models/eventhandler.go +++ b/models/eventhandler.go @@ -220,3 +220,24 @@ func (action *EventAction) Apply(other EventAction) { action.FireEvent = other.FireEvent } } + +func (c EventCondition) String() string { + str := make([]string, 0, 5) + if len(c.LT) > 0 { + str = append(str, "lt:" + c.LT) + } + if len(c.LTE) > 0 { + str = append(str, "lte:" + c.LTE) + } + if len(c.EQ) > 0 { + str = append(str, c.EQ) + } + if len(c.GTE) > 0 { + str = append(str, "gte:" + c.GTE) + } + if len(c.GT) > 0 { + str = append(str, "gte:" + c.GT) + } + + return strings.Join(str, ";") +} From b28496fd6b209ac1562468db43d88dc74c1860f4 Mon Sep 17 00:00:00 2001 From: Stian Fredrik Aune Date: Sat, 25 Sep 2021 17:27:56 +0200 Subject: [PATCH 2/4] less focus on oneshot --- cmd/lucy/tables.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/lucy/tables.go b/cmd/lucy/tables.go index 8e639ab..327dae0 100644 --- a/cmd/lucy/tables.go +++ b/cmd/lucy/tables.go @@ -76,7 +76,7 @@ func WriteDeviceInfoTable(w io.Writer, devices []models.Device) { func WriteHandlerInfoTable(w io.Writer, handlers []models.EventHandler) { table := tablewriter.NewWriter(w) - table.SetHeader([]string{"ID", "EVENT NAME", "ONE SHOT", "PRIORITY", "CONDITIONS", "TARGET", "ACTION"}) + table.SetHeader([]string{"ID", "EVENT NAME", "PRIORITY", "CONDITIONS", "TARGET", "ACTION"}) table.SetReflowDuringAutoWrap(false) for _, h := range handlers { @@ -102,10 +102,14 @@ func WriteHandlerInfoTable(w io.Writer, handlers []models.EventHandler) { actionStr += fmt.Sprintf("fireEvent=%s ", (*h.Actions.FireEvent).Name) } + eventName := h.EventName + if h.OneShot { + eventName += " (one shot)" + } + table.Append([]string{ strconv.Itoa(h.ID), - h.EventName, - strconv.FormatBool(h.OneShot), + eventName, strconv.Itoa(h.Priority), condStr, fmt.Sprintf("%s:%s", h.TargetKind, h.TargetValue), From b1fa52e53f59a3c1198f5143d366aa6c0372c298 Mon Sep 17 00:00:00 2001 From: Stian Fredrik Aune Date: Sat, 25 Sep 2021 19:03:11 +0200 Subject: [PATCH 3/4] lucy handler delete --- app/client/handler.go | 11 +++++++++++ cmd/lucy/handlercmd.go | 13 +++++++++++++ cmd/lucy/help.go | 5 ++++- install-lucy.sh | 2 +- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/client/handler.go b/app/client/handler.go index 009fc39..89200c0 100644 --- a/app/client/handler.go +++ b/app/client/handler.go @@ -2,6 +2,7 @@ package client import ( "context" + "fmt" "git.aiterp.net/lucifer/new-server/models" ) @@ -14,3 +15,13 @@ func (client *Client) GetHandlers(ctx context.Context, fetchStr string) ([]model return handlers, nil } + +func (client *Client) DeleteHandler(ctx context.Context, id int) (*models.EventHandler, error) { + var handler models.EventHandler + err := client.Fetch(ctx, "DELETE", fmt.Sprintf("/api/event-handlers/%d", id), &handler, nil) + if err != nil { + return nil, err + } + + return &handler, nil +} diff --git a/cmd/lucy/handlercmd.go b/cmd/lucy/handlercmd.go index 8410893..1f84dcd 100644 --- a/cmd/lucy/handlercmd.go +++ b/cmd/lucy/handlercmd.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "git.aiterp.net/lucifer/new-server/app/client" + "git.aiterp.net/lucifer/new-server/models" "log" "os" ) @@ -22,6 +23,18 @@ func handlerCmd( } WriteHandlerInfoTable(os.Stdout, handlers) + case "delete": + id := cmd.Params.Get(0).Int() + if id == nil { + log.Fatalln("ID missing") + } + + handler, err := c.DeleteHandler(ctx, *id) + if err != nil { + log.Fatalln(err) + } + + WriteHandlerInfoTable(os.Stdout, []models.EventHandler{*handler}) default: if cmd.Name != "help" { log.Println("Unknown command:", cmd.Name) diff --git a/cmd/lucy/help.go b/cmd/lucy/help.go index fc4a77b..4d77a2f 100644 --- a/cmd/lucy/help.go +++ b/cmd/lucy/help.go @@ -7,8 +7,11 @@ EXAMPLES lucy set tag:Hexagon color=hs:35,1 intensity=0.3 lucy run SetProfile name=evening +EVENT HANDLER COMMANDS + handler list + handler delete + DEVICE COMMANDS - handler list list set tag <[+/-]tag-name> diff --git a/install-lucy.sh b/install-lucy.sh index 8954ee0..d97adaa 100755 --- a/install-lucy.sh +++ b/install-lucy.sh @@ -5,7 +5,7 @@ go mod download || exit 1 go build -ldflags "-w -s" -o lucy ./cmd/lucy || exit 1 printf "Removing old lucy...\n" -sudo rm /usr/local/bin/lucy > /dev/null 2&>1 +sudo rm /usr/local/bin/lucy > /dev/null 2>&1 printf "Installing...\n" sudo mv ./lucy /usr/local/bin/lucy From 0cd325e8dbde35459196c0d74771df6d66a8ebe8 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sun, 26 Sep 2021 10:50:54 +0200 Subject: [PATCH 4/4] add SaveMode for device which can be 0 (All) or a bitmask for what to save. It did not improve performance --- app/api/devices.go | 8 ++-- app/services/events.go | 2 +- app/services/synclights.go | 2 +- internal/mysql/devicerepo.go | 91 ++++++++++++++++++++---------------- models/device.go | 14 ++++-- 5 files changed, 68 insertions(+), 49 deletions(-) diff --git a/app/api/devices.go b/app/api/devices.go index 275961f..4555772 100644 --- a/app/api/devices.go +++ b/app/api/devices.go @@ -80,7 +80,7 @@ func Devices(r gin.IRoutes) { go func() { for _, device := range changed { - err := config.DeviceRepository().Save(context.Background(), &device) + err := config.DeviceRepository().Save(context.Background(), &device, models.SMState) if err != nil { log.Println("Failed to save device for state:", err) continue @@ -109,7 +109,7 @@ func Devices(r gin.IRoutes) { for i := range devices { devices[i].ApplyUpdate(update) - err := config.DeviceRepository().Save(context.Background(), &devices[i]) + err := config.DeviceRepository().Save(context.Background(), &devices[i], models.SMProperties) if err != nil { log.Println("Failed to save device for state:", err) continue @@ -145,7 +145,7 @@ func Devices(r gin.IRoutes) { go func() { for _, device := range devices { - err := config.DeviceRepository().Save(context.Background(), &device) + err := config.DeviceRepository().Save(context.Background(), &device, models.SMState) if err != nil { log.Println("Failed to save device for state:", err) continue @@ -204,7 +204,7 @@ func Devices(r gin.IRoutes) { device.Tags = append(device.Tags[:index], device.Tags[index+1:]...) } - err = config.DeviceRepository().Save(ctxOf(c), device) + err = config.DeviceRepository().Save(ctxOf(c), device, models.SMTags) if err != nil { return nil, err } diff --git a/app/services/events.go b/app/services/events.go index b873924..c1bac4d 100644 --- a/app/services/events.go +++ b/app/services/events.go @@ -157,7 +157,7 @@ func handleEvent(event models.Event) (responses []models.Event) { wg.Add(1) go func(device models.Device) { - err := config.DeviceRepository().Save(context.Background(), &device) + err := config.DeviceRepository().Save(context.Background(), &device, models.SMState) if err != nil { log.Println("Failed to save device for state:", err) } diff --git a/app/services/synclights.go b/app/services/synclights.go index 1dc0cb2..11b9c80 100644 --- a/app/services/synclights.go +++ b/app/services/synclights.go @@ -66,7 +66,7 @@ func checkNewDevices() error { log.Println("Saving new device", driverDevice.InternalID) - err := config.DeviceRepository().Save(ctx, &driverDevice) + err := config.DeviceRepository().Save(ctx, &driverDevice, models.SMState|models.SMProperties|models.SMTags) if err != nil { log.Println("Failed to save device:", err) continue diff --git a/internal/mysql/devicerepo.go b/internal/mysql/devicerepo.go index e800ea5..1d6e373 100644 --- a/internal/mysql/devicerepo.go +++ b/internal/mysql/devicerepo.go @@ -117,7 +117,7 @@ func (r *DeviceRepo) FetchByReference(ctx context.Context, kind models.Reference return r.populate(ctx, records) } -func (r *DeviceRepo) Save(ctx context.Context, device *models.Device) error { +func (r *DeviceRepo) Save(ctx context.Context, device *models.Device, mode models.SaveMode) error { tx, err := r.DBX.Beginx() if err != nil { return dbErr(err) @@ -149,13 +149,18 @@ func (r *DeviceRepo) Save(ctx context.Context, device *models.Device) error { } // Let's just be lazy for now, optimize later if need be. - _, err = tx.ExecContext(ctx, "DELETE FROM device_tag WHERE device_id=?", record.ID) - if err != nil { - return dbErr(err) + if mode == 0 || mode&models.SMTags != 0 { + _, err = tx.ExecContext(ctx, "DELETE FROM device_tag WHERE device_id=?", record.ID) + if err != nil { + return dbErr(err) + } } - _, err = tx.ExecContext(ctx, "DELETE FROM device_property WHERE device_id=?", record.ID) - if err != nil { - return dbErr(err) + + if mode == 0 || mode&models.SMProperties != 0 { + _, err = tx.ExecContext(ctx, "DELETE FROM device_property WHERE device_id=?", record.ID) + if err != nil { + return dbErr(err) + } } } else { res, err := tx.NamedExecContext(ctx, ` @@ -175,51 +180,57 @@ func (r *DeviceRepo) Save(ctx context.Context, device *models.Device) error { device.ID = int(lastID) } - for _, tag := range device.Tags { - _, err := tx.ExecContext(ctx, "INSERT INTO device_tag (device_id, tag_name) VALUES (?, ?)", record.ID, tag) - if err != nil { - return dbErr(err) + if mode == 0 || mode&models.SMTags != 0 { + for _, tag := range device.Tags { + _, err := tx.ExecContext(ctx, "INSERT INTO device_tag (device_id, tag_name) VALUES (?, ?)", record.ID, tag) + if err != nil { + return dbErr(err) + } } } - for key, value := range device.UserProperties { - _, err := tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 1)", - record.ID, key, value, - ) - if err != nil { - return dbErr(err) + if mode == 0 || mode&models.SMProperties != 0 { + for key, value := range device.UserProperties { + _, err := tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 1)", + record.ID, key, value, + ) + if err != nil { + return dbErr(err) + } } - } - for key, value := range device.DriverProperties { - j, err := json.Marshal(value) - if err != nil { - // Eh, it'll get filled by the driver anyway - continue - } + for key, value := range device.DriverProperties { + j, err := json.Marshal(value) + if err != nil { + // Eh, it'll get filled by the driver anyway + continue + } - _, err = tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 0)", - record.ID, key, string(j), - ) - if err != nil { - // Return err here anyway, it might put the tx in a bad state to ignore it. - return dbErr(err) + _, err = tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 0)", + record.ID, key, string(j), + ) + if err != nil { + // Return err here anyway, it might put the tx in a bad state to ignore it. + return dbErr(err) + } } } - _, err = tx.NamedExecContext(ctx, ` + if mode == 0 || mode&models.SMState != 0 { + _, err = tx.NamedExecContext(ctx, ` REPLACE INTO device_state(device_id, hue, saturation, kelvin, power, intensity) VALUES (:device_id, :hue, :saturation, :kelvin, :power, :intensity) `, deviceStateRecord{ - DeviceID: record.ID, - Hue: device.State.Color.Hue, - Saturation: device.State.Color.Saturation, - Kelvin: device.State.Color.Kelvin, - Power: device.State.Power, - Intensity: device.State.Intensity, - }) - if err != nil { - return dbErr(err) + DeviceID: record.ID, + Hue: device.State.Color.Hue, + Saturation: device.State.Color.Saturation, + Kelvin: device.State.Color.Kelvin, + Power: device.State.Power, + Intensity: device.State.Intensity, + }) + if err != nil { + return dbErr(err) + } } return tx.Commit() diff --git a/models/device.go b/models/device.go index de51e88..e8e5146 100644 --- a/models/device.go +++ b/models/device.go @@ -20,8 +20,8 @@ type Device struct { } type DeviceUpdate struct { - Icon *string `json:"icon"` - Name *string `json:"name"` + Icon *string `json:"icon"` + Name *string `json:"name"` UserProperties map[string]*string `json:"userProperties"` } @@ -46,10 +46,18 @@ type NewDeviceState struct { type DeviceCapability string +type SaveMode int + +const ( + SMState SaveMode = 1 + SMProperties SaveMode = 2 + SMTags SaveMode = 4 +) + type DeviceRepository interface { Find(ctx context.Context, id int) (*Device, error) FetchByReference(ctx context.Context, kind ReferenceKind, value string) ([]Device, error) - Save(ctx context.Context, device *Device) error + Save(ctx context.Context, device *Device, mode SaveMode) error Delete(ctx context.Context, device *Device) error }