package main import ( "context" "git.aiterp.net/lucifer/new-server/app/client" "git.aiterp.net/lucifer/new-server/models" "gopkg.in/yaml.v2" "log" "os" "strconv" "strings" "unicode" ) func sceneCmd( ctx context.Context, c client.Client, ) { cmd := parseCommand(os.Args[2:]) switch cmd.Name { case "list": { scenes, err := c.GetScenes(ctx) if err != nil { log.Fatalln("Failed to load scenes:", err) } WriteSceneTable(os.Stdout, scenes) } case "create", "update": { fileName := cmd.Params.Get(0).String() if fileName == nil { log.Fatalln("Missing filename") } file, err := os.Open(*fileName) if err != nil { log.Fatalln("Failed to open file:", err) } defer file.Close() yamlData := make(map[string]interface{}) err = yaml.NewDecoder(file).Decode(yamlData) if err != nil { log.Fatalln("Failed to decode file:", err) return } yamlData = camelCasify(yamlData) name, nameOk := yamlData["name"] if !nameOk { log.Fatalln("Missing name in yaml data.") } var scene models.Scene if cmd.Name == "create" { err := c.Fetch(ctx, "POST", "/api/scenes", &scene, yamlData) if err != nil { log.Fatalln("Failed to create scene:", err) return } } else { scenes, err := c.GetScenes(ctx) if err != nil { log.Fatalln("Failed to fetch existing scenes:", err) return } id := -1 for _, scene := range scenes { if scene.Name == name { id = scene.ID break } } if id == -1 { log.Fatalln("Could not find scene with name", name) return } err = c.Fetch(ctx, "PUT", "/api/scenes/"+strconv.Itoa(id), &scene, yamlData) if err != nil { log.Fatalln("Failed to update scene:", err) return } } } case "push", "assign": { fetch := cmd.Params.Get(0).String() id := cmd.Params.Get(1).Int() if fetch == nil || id == nil { log.Println("Usage: lucy scene assign ") } devices, err := c.AssignDevice(ctx, *fetch, cmd.Name == "push", models.DeviceSceneAssignment{ SceneID: *id, Group: cmd.Params.Get("group").StringOr(*fetch), DurationMS: int64(cmd.Params.Get("duration").IntOr(0)), }) if err != nil { log.Println("Could not assign devices:", err) return } WriteDeviceInfoTable(os.Stdout, devices) } case "clear": fetch := cmd.Params.Get(0).String() if fetch == nil { log.Println("Usage: lucy scene clear ") } devices, err := c.ClearDevice(ctx, *fetch) if err != nil { log.Println("Could not clear devices:", err) return } WriteDeviceInfoTable(os.Stdout, devices) } } func camelCasify(m map[string]interface{}) map[string]interface{} { m2 := make(map[string]interface{}, len(m)) for key, value := range m { b := strings.Builder{} snake := false for _, ch := range key { if ch == '_' { snake = true } else if snake { b.WriteRune(unicode.ToUpper(ch)) snake = false } else { b.WriteRune(ch) } } switch value := value.(type) { case []interface{}: valueCopy := make([]interface{}, len(value)) for i, elem := range value { switch elem := elem.(type) { case map[interface{}]interface{}: m3 := make(map[string]interface{}) for k, v := range elem { if kStr, ok := k.(string); ok { m3[kStr] = v } } valueCopy[i] = camelCasify(m3) case map[string]interface{}: valueCopy[i] = camelCasify(elem) default: valueCopy[i] = elem } } m2[b.String()] = valueCopy case map[string]interface{}: m2[b.String()] = camelCasify(value) default: m2[b.String()] = value } } return m2 }