From c7d7e264cab67302f22cd71342e9010dc30689f1 Mon Sep 17 00:00:00 2001 From: Stian Fredrik Aune Date: Tue, 28 Sep 2021 21:54:03 +0200 Subject: [PATCH] Lucy handler add --- app/api/devices.go | 22 ++---------- app/client/handler.go | 12 ++++++- app/services/events.go | 4 +++ cmd/lucy/command.go | 77 +++++++++++++++++++++++++++++++++++++----- cmd/lucy/handlercmd.go | 58 ++++++++++++++++++++++++++++++- cmd/lucy/help.go | 17 +++++++--- cmd/lucy/main.go | 14 ++++---- cmd/lucy/tables.go | 3 ++ models/eventhandler.go | 11 +++--- models/shared.go | 26 ++++++++++++++ 10 files changed, 197 insertions(+), 47 deletions(-) diff --git a/app/api/devices.go b/app/api/devices.go index 4555772..f77dced 100644 --- a/app/api/devices.go +++ b/app/api/devices.go @@ -6,29 +6,11 @@ import ( "git.aiterp.net/lucifer/new-server/models" "github.com/gin-gonic/gin" "log" - "strconv" - "strings" ) func fetchDevices(ctx context.Context, fetchStr string) ([]models.Device, error) { - if strings.HasPrefix(fetchStr, "tag:") { - return config.DeviceRepository().FetchByReference(ctx, models.RKTag, fetchStr[4:]) - } else if strings.HasPrefix(fetchStr, "bridge:") { - return config.DeviceRepository().FetchByReference(ctx, models.RKBridgeID, fetchStr[7:]) - } else if strings.HasPrefix(fetchStr, "id:") { - return config.DeviceRepository().FetchByReference(ctx, models.RKDeviceID, fetchStr[7:]) - } else if strings.HasPrefix(fetchStr, "name:") { - return config.DeviceRepository().FetchByReference(ctx, models.RKName, fetchStr[7:]) - }else if fetchStr == "all" { - return config.DeviceRepository().FetchByReference(ctx, models.RKAll, "") - } else { - _, err := strconv.Atoi(fetchStr) - if err != nil { - return config.DeviceRepository().FetchByReference(ctx, models.RKName, fetchStr) - } - - return config.DeviceRepository().FetchByReference(ctx, models.RKDeviceID, fetchStr) - } + kind, value := models.ParseFetchString(fetchStr) + return config.DeviceRepository().FetchByReference(ctx, kind, value) } func Devices(r gin.IRoutes) { diff --git a/app/client/handler.go b/app/client/handler.go index 89200c0..b0b4fdd 100644 --- a/app/client/handler.go +++ b/app/client/handler.go @@ -6,7 +6,7 @@ import ( "git.aiterp.net/lucifer/new-server/models" ) -func (client *Client) GetHandlers(ctx context.Context, fetchStr string) ([]models.EventHandler, error) { +func (client *Client) GetHandlers(ctx context.Context) ([]models.EventHandler, error) { handlers := make([]models.EventHandler, 0, 16) err := client.Fetch(ctx, "GET", "/api/event-handlers", &handlers, nil) if err != nil { @@ -16,6 +16,16 @@ func (client *Client) GetHandlers(ctx context.Context, fetchStr string) ([]model return handlers, nil } +func (client *Client) AddHandler(ctx context.Context, handler models.EventHandler) (*models.EventHandler, error) { + var result models.EventHandler + err := client.Fetch(ctx, "POST", "/api/event-handlers", &result, handler) + if err != nil { + return nil, err + } + + return &result, 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) diff --git a/app/services/events.go b/app/services/events.go index c1bac4d..eb81679 100644 --- a/app/services/events.go +++ b/app/services/events.go @@ -144,6 +144,10 @@ func handleEvent(event models.Event) (responses []models.Event) { newState.Intensity = &newIntensity } + if action.SetTemperature != nil { + newState.Temperature = action.SetTemperature + } + err = allDevices[i].SetState(newState) if err != nil { log.Println("Error updating state for device", device.ID, "err:", err) diff --git a/cmd/lucy/command.go b/cmd/lucy/command.go index 9e1af72..4c9b033 100644 --- a/cmd/lucy/command.go +++ b/cmd/lucy/command.go @@ -1,15 +1,17 @@ package main import ( + "git.aiterp.net/lucifer/new-server/models" "log" "strconv" "strings" ) type Param struct { - Index int - Key string - Value string + Index int + Key string + Value string + Operator string } func (p *Param) String() *string { @@ -28,7 +30,6 @@ func (p *Param) StringOr(fallback string) string { return p.Value } - func (p *Param) Int() *int { if p == nil { return nil @@ -42,6 +43,15 @@ func (p *Param) Int() *int { return &n } +func (p *Param) IntOr(other int) int { + val := p.Int() + if val == nil { + return other + } + + return *val +} + func (p *Param) Float() *float64 { if p == nil { return nil @@ -108,7 +118,7 @@ func (p Params) Subset(prefix string) Params { for _, param := range p { if param.Index == -1 && strings.HasPrefix(param.Key, prefix) { - res = append(res, Param{Index: -1, Key: param.Key[len(prefix):], Value: param.Value}) + res = append(res, Param{Index: -1, Key: param.Key[len(prefix):], Value: param.Value, Operator: param.Operator}) } } @@ -152,6 +162,34 @@ func (p Params) Strings(minIndex int) []string { return res } +func (p Params) EventConditions() map[string]models.EventCondition { + ecMap := make(map[string]models.EventCondition, len(p)) + + for _, param := range p { + element, ok := ecMap[param.Key] + if !ok { + element = models.EventCondition{} + } + + switch param.Operator { + case "<": + element.LT = param.Value + case "<=": + element.LTE = param.Value + case ">=": + element.GTE = param.Value + case ">": + element.GT = param.Value + default: + element.EQ = param.Value + } + + ecMap[param.Key] = element + } + + return ecMap +} + type Command struct { Name string Params Params @@ -169,10 +207,22 @@ func parseCommand(args []string) Command { nextIndex := 0 for _, arg := range args[1:] { - kv := strings.SplitN(arg, "=", 2) - - if len(kv) == 2 { - cmd.Params = append(cmd.Params, Param{Index: -1, Key: kv[0], Value: kv[1]}) + kvle := strings.SplitN(arg, "<=", 2) + kvge := strings.SplitN(arg, ">=", 2) + kvl := strings.SplitN(arg, "<", 2) + kvg := strings.SplitN(arg, ">", 2) + kve := strings.SplitN(arg, "=", 2) + + if len(kvle) == 2 { + cmd.Params = append(cmd.Params, Param{Index: -1, Key: kvle[0], Value: kvle[1], Operator: "<="}) + } else if len(kvge) == 2 { + cmd.Params = append(cmd.Params, Param{Index: -1, Key: kvge[0], Value: kvge[1], Operator: ">="}) + } else if len(kvl) == 2 { + cmd.Params = append(cmd.Params, Param{Index: -1, Key: kvl[0], Value: kvl[1], Operator: "<"}) + } else if len(kvg) == 2 { + cmd.Params = append(cmd.Params, Param{Index: -1, Key: kvg[0], Value: kvg[1], Operator: ">"}) + } else if len(kve) == 2 { + cmd.Params = append(cmd.Params, Param{Index: -1, Key: kve[0], Value: kve[1], Operator: "="}) } else { cmd.Params = append(cmd.Params, Param{Index: nextIndex, Value: arg}) nextIndex += 1 @@ -181,3 +231,12 @@ func parseCommand(args []string) Command { return cmd } + +func (cmd Command) NewDeviceState(prefix string) models.NewDeviceState { + return models.NewDeviceState{ + Power: cmd.Params.Get(prefix + "power").Bool(), + Color: cmd.Params.Get(prefix + "color").String(), + Intensity: cmd.Params.Get(prefix + "intensity").Float(), + Temperature: cmd.Params.Get(prefix + "temperature").Int(), + } +} diff --git a/cmd/lucy/handlercmd.go b/cmd/lucy/handlercmd.go index 1f84dcd..1081f7e 100644 --- a/cmd/lucy/handlercmd.go +++ b/cmd/lucy/handlercmd.go @@ -17,12 +17,68 @@ func handlerCmd( switch cmd.Name { case "list": - handlers, err := c.GetHandlers(ctx, cmd.Params.Get(0).StringOr("all")) + handlers, err := c.GetHandlers(ctx) if err != nil { log.Fatalln(err) } WriteHandlerInfoTable(os.Stdout, handlers) + case "add": + model := models.EventHandler{} + + // Event name + model.EventName = cmd.Params.Get(0).StringOr("") + if len(model.EventName) == 0 { + log.Fatalln("Event name needed!") + } + + // Search + fetchStr := cmd.Params.Get(1).String() + if fetchStr == nil { + log.Fatalln("Search filter needed!") + } + model.TargetKind, model.TargetValue = models.ParseFetchString(*fetchStr) + + // One shot + model.OneShot = cmd.Params.Get("one-shot").StringOr("false") == "true" + + // Priority + model.Priority = cmd.Params.Get("priority").IntOr(100) + + // Conditions + model.Conditions = cmd.Params.Subset("conditions").EventConditions() + + // Power, Color, Intensity, Temperature + nds := cmd.NewDeviceState("set-") + model.Actions.SetPower = nds.Power + model.Actions.SetColor = nds.Color + model.Actions.SetIntensity = nds.Intensity + + // Add intensity + model.Actions.AddIntensity = cmd.Params.Get("add-intensity").Float() + + // Fire + fireParams := cmd.Params.Subset("fire") + fireName := fireParams.Get("name").String() + if fireName != nil { + firePayloadParams := fireParams.Subset("payload") + + model.Actions.FireEvent = &models.Event{ + Name: *fireName, + Payload: make(map[string]string, len(firePayloadParams)), + } + + for key, value := range firePayloadParams.StringMap() { + model.Actions.FireEvent.Payload[key] = value + } + } + + newHandler, err := c.AddHandler(ctx, model) + if err != nil { + log.Fatalln(err) + } + + WriteHandlerInfoTable(os.Stdout, []models.EventHandler{*newHandler}) case "delete": id := cmd.Params.Get(0).Int() if id == nil { diff --git a/cmd/lucy/help.go b/cmd/lucy/help.go index 4d77a2f..e94f138 100644 --- a/cmd/lucy/help.go +++ b/cmd/lucy/help.go @@ -7,16 +7,25 @@ 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 list set tag <[+/-]tag-name> update +EVENT HANDLER COMMANDS + handler list + handler add \ + \ + \ + |>]*> \ + \ + \ + + handler delete + EVENT COMMANDS run <*=S> + +NOTE: You may have to surround arguments including < or > with quotation marks! ` diff --git a/cmd/lucy/main.go b/cmd/lucy/main.go index fb2baa1..887acac 100644 --- a/cmd/lucy/main.go +++ b/cmd/lucy/main.go @@ -37,16 +37,15 @@ func main() { log.Fatalln(err) } - WriteDeviceStateTable(os.Stdout, devices) + if cmd.Params.Get("info").StringOr("false") == "true" { + WriteDeviceInfoTable(os.Stdout, devices) + } else { + WriteDeviceStateTable(os.Stdout, devices) + } } case "set": { - 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(), - Temperature: cmd.Params.Get("temperature").Int(), - }) + devices, err := c.PutDeviceState(ctx, cmd.Params.Get(0).StringOr("all"), cmd.NewDeviceState("")) if err != nil { log.Fatalln(err) } @@ -124,3 +123,4 @@ func main() { _, _ = fmt.Fprintln(os.Stderr, helpString[1:]) } } + diff --git a/cmd/lucy/tables.go b/cmd/lucy/tables.go index 327dae0..cb3a15b 100644 --- a/cmd/lucy/tables.go +++ b/cmd/lucy/tables.go @@ -95,6 +95,9 @@ func WriteHandlerInfoTable(w io.Writer, handlers []models.EventHandler) { if h.Actions.SetIntensity != nil { actionStr += fmt.Sprintf("setIntensity=%.02f ", *h.Actions.SetIntensity) } + if h.Actions.SetTemperature != nil { + actionStr += fmt.Sprintf("setTemperature=%d ", *h.Actions.SetTemperature) + } if h.Actions.AddIntensity != nil { actionStr += fmt.Sprintf("addIntensity=%.02f ", *h.Actions.AddIntensity) } diff --git a/models/eventhandler.go b/models/eventhandler.go index 840ff50..1651d4b 100644 --- a/models/eventhandler.go +++ b/models/eventhandler.go @@ -196,11 +196,12 @@ func (c *EventCondition) matches(value string) bool { } type EventAction struct { - SetPower *bool `json:"setPower"` - SetColor *string `json:"setColor"` - SetIntensity *float64 `json:"setIntensity"` - AddIntensity *float64 `json:"addIntensity"` - FireEvent *Event `json:"fireEvent"` + SetPower *bool `json:"setPower"` + SetColor *string `json:"setColor"` + SetIntensity *float64 `json:"setIntensity"` + SetTemperature *int `json:"setTemperature"` + AddIntensity *float64 `json:"addIntensity"` + FireEvent *Event `json:"fireEvent"` } func (action *EventAction) Apply(other EventAction) { diff --git a/models/shared.go b/models/shared.go index f92a20f..3917b58 100644 --- a/models/shared.go +++ b/models/shared.go @@ -1,5 +1,10 @@ package models +import ( + "strconv" + "strings" +) + type ReferenceKind string var ( @@ -9,3 +14,24 @@ var ( RKAll ReferenceKind = "All" RKName ReferenceKind = "Name" ) + +func ParseFetchString(fetchStr string) (ReferenceKind, string) { + if strings.HasPrefix(fetchStr, "tag:") { + return RKTag, fetchStr[4:] + } else if strings.HasPrefix(fetchStr, "bridge:") { + return RKBridgeID, fetchStr[7:] + } else if strings.HasPrefix(fetchStr, "id:") { + return RKDeviceID, fetchStr[7:] + } else if strings.HasPrefix(fetchStr, "name:") { + return RKName, fetchStr[7:] + } else if fetchStr == "all" { + return RKAll, "" + } else { + _, err := strconv.Atoi(fetchStr) + if err != nil { + return RKName, fetchStr + } + + return RKDeviceID, fetchStr + } +}