Skip to content

Commit

Permalink
Implement 'POST /connect-all?wait=...&iface=...' (#197)
Browse files Browse the repository at this point in the history
* Implement 'POST /connect-all?wait=...&iface=...'

It's common use-case to connect to all available chromecasts when you have local server.

* Added autoconnect option.

* Clarify usage instruction.
  • Loading branch information
ptabor authored Nov 25, 2024
1 parent b750a18 commit 970967f
Showing 1 changed file with 76 additions and 12 deletions.
88 changes: 76 additions & 12 deletions http/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,30 @@ type Handler struct {
apps map[string]application.App
mux *http.ServeMux

verbose bool
verbose bool
autoconnect bool
}

func NewHandler(verbose bool) *Handler {
handler := &Handler{
verbose: verbose,
apps: map[string]application.App{},
mux: http.NewServeMux(),
mu: sync.Mutex{},
verbose: verbose,
apps: map[string]application.App{},
mux: http.NewServeMux(),
mu: sync.Mutex{},
autoconnect: false,
}
handler.registerHandlers()
return handler
}

// Autoconnect configures the handler to perform auto-discovery of all the cast devices & groups.
// It's intended to be called just after `NewHandler()`, before the handler is registered in the server.
func (h *Handler) Autoconnect() error {
// Setting the autoconnect property - to allow (in future) periodic refresh of the connections.
h.autoconnect = true
return h.connectAllInternal("", "3")
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.mux.ServeHTTP(w, r)
}
Expand All @@ -45,8 +55,9 @@ func (h *Handler) Serve(addr string) error {

func (h *Handler) registerHandlers() {
/*
GET /devices
GET /devices?wait=...&iface=...
POST /connect?uuid=<device_uuid>&addr=<device_addr>&port=<device_port>
POST /connect-all?wait=...&iface=...
POST /disconnect?uuid=<device_uuid>
POST /disconnect-all
POST /status?uuid=<device_uuid>
Expand All @@ -66,6 +77,7 @@ func (h *Handler) registerHandlers() {

h.mux.HandleFunc("/devices", h.listDevices)
h.mux.HandleFunc("/connect", h.connect)
h.mux.HandleFunc("/connect-all", h.connectAll)
h.mux.HandleFunc("/disconnect", h.disconnect)
h.mux.HandleFunc("/disconnect-all", h.disconnectAll)
h.mux.HandleFunc("/status", h.status)
Expand Down Expand Up @@ -201,28 +213,80 @@ func (h *Handler) connect(w http.ResponseWriter, r *http.Request) {
return
}

app, err := h.connectInternal(deviceAddr, devicePortI)
if err != nil {
h.log("unable to start application: %v", err)
httpError(w, fmt.Errorf("unable to start application: %v", err))
return
}
h.mu.Lock()
h.apps[deviceUUID] = app
h.mu.Unlock()

w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(connectResponse{DeviceUUID: deviceUUID}); err != nil {
h.log("error encoding json: %v", err)
httpError(w, fmt.Errorf("unable to json encode devices: %v", err))
return
}
}

func (h *Handler) connectInternal(deviceAddr string, devicePort int) (application.App, error) {
applicationOptions := []application.ApplicationOption{
application.WithDebug(h.verbose),
application.WithCacheDisabled(true),
}

app := application.NewApplication(applicationOptions...)
if err := app.Start(deviceAddr, devicePortI); err != nil {
h.log("unable to start application: %v", err)
httpError(w, fmt.Errorf("unable to start application: %v", err))
return
if err := app.Start(deviceAddr, devicePort); err != nil {
return nil, err
}
return app, nil
}

func (h *Handler) connectAll(w http.ResponseWriter, r *http.Request) {
h.log("connecting all devices")
q := r.URL.Query()

wait := q.Get("wait")
iface := q.Get("interface")

err := h.connectAllInternal(iface, wait)
if err != nil {
h.log("error connecting: %v", err)
httpError(w, fmt.Errorf("unable to connect to device: %v", err))
}

var resp []connectResponse
h.mu.Lock()
h.apps[deviceUUID] = app
for deviceUUID, _ := range h.apps {
resp = append(resp, connectResponse{DeviceUUID: deviceUUID})
}
h.mu.Unlock()

w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(connectResponse{DeviceUUID: deviceUUID}); err != nil {
if err := json.NewEncoder(w).Encode(resp); err != nil {
h.log("error encoding json: %v", err)
httpError(w, fmt.Errorf("unable to json encode devices: %v", err))
return
}
}

func (h *Handler) connectAllInternal(iface string, waitSec string) error {
devices := h.discoverDnsEntries(context.Background(), iface, waitSec)
uuidMap := map[string]application.App{}
for _, device := range devices {
app, err := h.connectInternal(device.Addr, device.Port)
if err != nil {
return err
}
uuidMap[device.UUID] = app
}

h.mu.Lock()
h.apps = uuidMap
h.mu.Unlock()
return nil
}

func (h *Handler) disconnect(w http.ResponseWriter, r *http.Request) {
Expand Down

0 comments on commit 970967f

Please sign in to comment.