package main import ( "fmt" "os" "os/exec" "os/signal" "strings" "syscall" mqtt "github.com/eclipse/paho.mqtt.golang" "gopkg.in/yaml.v3" ) var config = Config{} var onCommandMessage mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) { value := string(msg.Payload()) fmt.Printf("Requested %s\n", value) soundDef, found := config.Player.Sounds[value] if !found { soundDef, found = config.Player.Sounds["default"] if !found { fmt.Fprintf(os.Stderr, "No config entry found\n") return } } token := client.Publish(config.Mqtt.StateTopic, 0, false, "playing") token.Wait() var args []string var program string if soundDef.Program == "" { program = config.Player.DefaultProgram args = make([]string, len(config.Player.DefaultArgs)) copy(args, config.Player.DefaultArgs) args = append(args, soundDef.Args...) } else { program = soundDef.Program args = make([]string, len(soundDef.Args)) copy(args, soundDef.Args) } for i := range args { args[i] = strings.ReplaceAll(args[i], "{payload}", value) } fmt.Printf("Running %v %v\n", program, args) cmd := exec.Command(program, args...) cmd.Stderr = os.Stderr stdout, err := cmd.Output() out := strings.TrimSpace(string(stdout)) if err != nil { fmt.Fprintf(os.Stderr, "Execution error: %v\n", err.Error()) } if out != "" { fmt.Printf("Command output: %s\n", out) } token = client.Publish(config.Mqtt.StateTopic, 0, false, "stopped") token.Wait() } var onConnected mqtt.OnConnectHandler = func(client mqtt.Client) { fmt.Println("Connected") token := client.Subscribe(config.Mqtt.CommandTopic, 1, onCommandMessage) token.Wait() } var onDisconnected mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) { fmt.Fprintf(os.Stderr, "Connection lost: %v\n", err) } func main() { if len(os.Args) < 2 { fmt.Fprintf(os.Stderr, "Usage: %s config.yaml\n", os.Args[0]) os.Exit(2) } yamlData, err := os.ReadFile(os.Args[1]) if err != nil { panic(err.Error()) } yamlStr := os.ExpandEnv(string(yamlData)) err = yaml.Unmarshal([]byte(yamlStr), &config) if err != nil { panic(err.Error()) } opts := mqtt.NewClientOptions() opts.AddBroker(config.Mqtt.Address) opts.SetClientID(config.Mqtt.ClientId) opts.SetUsername(config.Mqtt.Username) opts.SetPassword(config.Mqtt.Password) opts.SetAutoReconnect(true) opts.OnConnect = onConnected opts.OnConnectionLost = onDisconnected client := mqtt.NewClient(opts) if token := client.Connect(); token.Wait() && token.Error() != nil { panic(token.Error()) } // wait forever for interrupt exit := make(chan os.Signal, 1) defer close(exit) signal.Notify(exit, syscall.SIGINT, syscall.SIGTERM) <-exit client.Disconnect(250) }