Skip to content

Commit

Permalink
Get supported remotes from protos given on the command line
Browse files Browse the repository at this point in the history
  • Loading branch information
mtraver committed Sep 3, 2019
1 parent ff970da commit 54f3b5c
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 117 deletions.
52 changes: 38 additions & 14 deletions cmd/cli/main.go
Expand Up @@ -8,8 +8,9 @@ import (
"sort"
"strings"

"github.com/golang/protobuf/jsonpb"
ipb "github.com/mtraver/rpi-ir-remote/irremotepb"
"github.com/mtraver/rpi-ir-remote/remote"
"github.com/mtraver/rpi-ir-remote/remote/cambridgecxacn"
)

var (
Expand All @@ -18,47 +19,55 @@ var (
sendCommand = flag.NewFlagSet("send", flag.ExitOnError)
repeat int

remotes = []remote.Remote{cambridgecxacn.New()}
remotes = []ipb.Remote{}
)

func init() {
listCommand.Usage = func() {
fmt.Println(`list: list available remotes and their commands
fmt.Fprintln(flag.CommandLine.Output(), `list: list available remotes and their commands
usage: list`)
listCommand.PrintDefaults()
}

sendCommand.IntVar(&repeat, "repeat", 0, "number of times to repeat command")
sendCommand.Usage = func() {
fmt.Println(`send: send an IR code
fmt.Fprintln(flag.CommandLine.Output(), `send: send an IR code
usage: send [options] remote command`)
sendCommand.PrintDefaults()
}

flag.Usage = func() {
fmt.Printf("usage: %s {list,send} [options] [args]\n", filepath.Base(os.Args[0]))
fmt.Println("\nCommands:")
message := `usage: %s remote_proto {list,send} [options] [args]
Positional arguments (required):
remote_proto
path to file containing a JSON-encoded remote proto
Commands:
`

fmt.Fprintf(flag.CommandLine.Output(), message, filepath.Base(os.Args[0]))
listCommand.Usage()
sendCommand.Usage()
}
}

func getRemote(name string) (remote.Remote, error) {
func getRemote(name string) (ipb.Remote, error) {
for _, r := range remotes {
if r.Name == name {
return r, nil
}
}

return remote.Remote{}, fmt.Errorf("cli: no remote with name %q", name)
return ipb.Remote{}, fmt.Errorf("cli: no remote with name %q", name)
}

func list() {
sort.Slice(remotes, func(i, j int) bool { return remotes[i].Name < remotes[j].Name })

strs := make([]string, len(remotes))
for i, r := range remotes {
strs[i] = strings.TrimRight(fmt.Sprintf("%v", r), "\n")
strs[i] = strings.TrimRight(remote.String(r), "\n")
}

fmt.Println(strings.Join(strs, "\n\n"))
Expand All @@ -71,27 +80,42 @@ func send(remoteName, commandName string, repeat int) {
os.Exit(2)
}

if err := r.Send(commandName); err != nil {
if err := remote.Send(r, commandName); err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
}

func main() {
if len(os.Args) < 2 {
if len(os.Args) < 3 {
flag.Usage()
os.Exit(2)
}

switch subcmd := os.Args[1]; subcmd {
// Parse remote proto.
rp := os.Args[1]
file, err := os.Open(rp)
if err != nil {
fmt.Printf("Failed to open remote proto %s: %v\n", rp, err)
os.Exit(1)
}
defer file.Close()
var r ipb.Remote
if err := jsonpb.Unmarshal(file, &r); err != nil {
fmt.Printf("Failed to parse remote proto %s: %v\n", rp, err)
os.Exit(1)
}
remotes = append(remotes, r)

switch subcmd := os.Args[2]; subcmd {
case "list":
if err := listCommand.Parse(os.Args[2:]); err == flag.ErrHelp {
if err := listCommand.Parse(os.Args[3:]); err == flag.ErrHelp {
listCommand.Usage()
}

list()
case "send":
sendCommand.Parse(os.Args[2:])
sendCommand.Parse(os.Args[3:])

if sendCommand.NArg() != 2 {
sendCommand.Usage()
Expand Down
94 changes: 62 additions & 32 deletions cmd/server/main.go
Expand Up @@ -16,14 +16,14 @@ import (
"time"

mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"github.com/mtraver/iotcore"

serverconfig "github.com/mtraver/rpi-ir-remote/cmd/server/config"
cpb "github.com/mtraver/rpi-ir-remote/cmd/server/configpb"
ipb "github.com/mtraver/rpi-ir-remote/irremotepb"
"github.com/mtraver/rpi-ir-remote/remote"
"github.com/mtraver/rpi-ir-remote/remote/cambridgecxacn"
)

const (
Expand All @@ -41,18 +41,27 @@ var (

templates = template.Must(template.New("index").Parse(indexTemplate))

// TODO(mtraver) Get supported remotes from protos given on the command line
remotes = make(map[string]remote.Remote)
remotes = make(map[string]ipb.Remote)
)

func init() {
flag.StringVar(&configFilePath, "config", "", "path to config file")
flag.StringVar(&deviceFilePath, "device", "", "path to a file containing a JSON-encoded Device struct (see github.com/mtraver/iotcore)")
flag.StringVar(&caCerts, "cacerts", "", "Path to a set of trustworthy CA certs.\nDownload Google's from https://pki.google.com/roots.pem.")

// TODO(mtraver) Get supported remotes from protos given on the command line
r := cambridgecxacn.New()
remotes[r.Name] = r
flag.Usage = func() {
message := `usage: server [options] remote_proto [remote_proto [remote_proto ...]]:
Positional arguments (required):
remote_proto
path to file containing a JSON-encoded remote proto
Options:
`

fmt.Fprintf(flag.CommandLine.Output(), message)
flag.PrintDefaults()
}
}

type irRemoteRequest struct {
Expand All @@ -61,7 +70,7 @@ type irRemoteRequest struct {
}

type irsendHandler struct {
Remote remote.Remote
Remote ipb.Remote
Config cpb.Config
Cmd string
CheckToken bool
Expand Down Expand Up @@ -101,7 +110,7 @@ func (h irsendHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

log.Printf("Received request: %v %v", h.Remote.Name, h.Cmd)

if err := h.Remote.Send(h.Cmd); err != nil {
if err := remote.Send(h.Remote, h.Cmd); err != nil {
log.Printf("Failed to send command %q to %q: %v", h.Cmd, h.Remote.Name, err)
w.WriteHeader(http.StatusInternalServerError)
return
Expand All @@ -112,8 +121,8 @@ func (h irsendHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

type indexHandler struct {
Remote remote.Remote
Config cpb.Config
Remotes map[string]ipb.Remote
Config cpb.Config
}

func (h indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Expand All @@ -123,11 +132,11 @@ func (h indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

data := struct {
Remote remote.Remote
Remotes map[string]ipb.Remote
Config cpb.Config
FunFact string
}{
Remote: h.Remote,
Remotes: h.Remotes,
Config: h.Config,
FunFact: funFacts[rand.Intn(len(funFacts))],
}
Expand Down Expand Up @@ -158,7 +167,7 @@ func commandHandler(client mqtt.Client, msg mqtt.Message) {
return
}

if err := r.Send(action.GetCommand()); err != nil {
if err := remote.Send(r, action.GetCommand()); err != nil {
log.Printf("commandHandler: failed to send command %q to %q: %v", action.GetCommand(), r.Name, err)
return
}
Expand Down Expand Up @@ -220,11 +229,33 @@ func main() {
os.Exit(2)
}

if len(flag.Args()) < 1 {
fmt.Fprintf(flag.CommandLine.Output(), "At least one remote proto must be given\n")
flag.Usage()
os.Exit(2)
}

// Parse config.
config, err := serverconfig.Load(configFilePath)
if err != nil {
log.Fatalf("Failed to parse config file %s: %v", configFilePath, err)
}

// Parse remote protos.
for _, rp := range flag.Args() {
file, err := os.Open(rp)
if err != nil {
log.Fatalf("Failed to open remote proto %s: %v", rp, err)
}
defer file.Close()

var r ipb.Remote
if err := jsonpb.Unmarshal(file, &r); err != nil {
log.Fatalf("Failed to parse remote proto %s: %v", rp, err)
}
remotes[r.Name] = r
}

// If an MQTT device config was given, connect to the MQTT broker. In the connect handler
// we'll subscribe to the commands topic.
if deviceFilePath != "" {
Expand All @@ -250,30 +281,29 @@ func main() {
}()
}

// TODO(mtraver) Get supported remotes from protos given on the command line
r := cambridgecxacn.New()

webuiMux := http.NewServeMux()
webuiMux.Handle("/", indexHandler{
Remote: r,
Config: config,
Remotes: remotes,
Config: config,
})

apiMux := http.NewServeMux()
for name := range r.Commands {
apiMux.Handle(fmt.Sprintf("/%v", name), irsendHandler{
Remote: r,
Config: config,
Cmd: name,
CheckToken: true,
})

webuiMux.Handle(fmt.Sprintf("/%v", name), irsendHandler{
Remote: r,
Config: config,
Cmd: name,
CheckToken: false,
})
for _, r := range remotes {
for _, code := range r.Code {
apiMux.Handle(fmt.Sprintf("/%v/%v", r.Name, code.Name), irsendHandler{
Remote: r,
Config: config,
Cmd: code.Name,
CheckToken: true,
})

webuiMux.Handle(fmt.Sprintf("/%v/%v", r.Name, code.Name), irsendHandler{
Remote: r,
Config: config,
Cmd: code.Name,
CheckToken: false,
})
}
}

// If an MQTT device config was not given, start the API HTTP server.
Expand Down
11 changes: 8 additions & 3 deletions cmd/server/template.go
Expand Up @@ -19,10 +19,15 @@ const indexTemplate = `
<div class="container mt-1">
<div class="col-sm text-center">
<p><b>Fun fact!</b> {{ .FunFact }}</p>
<p>Remote: {{ .Remote.Name }}</p>
{{ range $remoteName, $remote := .Remotes }}
<p><b>{{ $remoteName }}</b></p>
{{ range $name, $command := .Remote.Commands }}
<button type="button" class="btn btn-primary" name="{{ $name }}" id="button-{{ $name }}">{{ $name }}</button>
{{ range $i, $code := $remote.Code }}
<button type="button" class="btn btn-primary" name="{{ $remoteName }}/{{ $code.Name }}" id="button-{{ $remoteName }}-{{ $code.Name }}">{{ $code.Name }}</button>
<br>
<br>
{{ end }}
<br>
<br>
<br>
{{ end }}
Expand Down
33 changes: 33 additions & 0 deletions config/cambridge_cxa.pb.json
@@ -0,0 +1,33 @@
{
"name": "cambridge_cxa",
"code": [
{
"name": "off",
"code": "KEY_POWER_OFF"
},
{
"name": "music",
"code": "KEY_SOURCE_D1"
},
{
"name": "tv",
"code": "KEY_SOURCE_D2"
},
{
"name": "volup",
"code": "KEY_VOLUMEUP"
},
{
"name": "voldown",
"code": "KEY_VOLUMEDOWN"
},
{
"name": "direct",
"code": "KEY_DIRECT"
},
{
"name": "lcd",
"code": "KEY_LCD"
}
]
}

0 comments on commit 54f3b5c

Please sign in to comment.