Browse Source

Lucy handler add

feature-colorvalue2
Stian Fredrik Aune 3 years ago
parent
commit
c7d7e264ca
  1. 22
      app/api/devices.go
  2. 12
      app/client/handler.go
  3. 4
      app/services/events.go
  4. 77
      cmd/lucy/command.go
  5. 58
      cmd/lucy/handlercmd.go
  6. 17
      cmd/lucy/help.go
  7. 14
      cmd/lucy/main.go
  8. 3
      cmd/lucy/tables.go
  9. 11
      models/eventhandler.go
  10. 26
      models/shared.go

22
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) {

12
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)

4
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)

77
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(),
}
}

58
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 {

17
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 <id>
DEVICE COMMANDS
list <search>
set <search> <power=B> <color=C> <intensity=F> <temperature=N>
tag <search> <[+/-]tag-name>
update <search> <name=S> <icon=S> <prop.*=S/NULL>
EVENT HANDLER COMMANDS
handler list
handler add <event> <search> \
<one-shot=B> \
<priority=N> \
<conditions.[any.*|all.*|*][<|<=|=|=>|>]*> \
<set-power=B> <set-color=C> <set-intensity=F> <set-temperature=N> \
<add-intensity=F> \
<fire.name=S> <fire.payload.*=S>
handler delete <id>
EVENT COMMANDS
run <event name> <*=S>
NOTE: You may have to surround arguments including < or > with quotation marks!
`

14
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:])
}
}

3
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)
}

11
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) {

26
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
}
}
Loading…
Cancel
Save