main
Raw Download raw file
 1package protect
 2
 3import (
 4	"context"
 5	"fmt"
 6	"strings"
 7)
 8
 9// Bootstrap fetches system information including all cameras.
10func (c *Client) Bootstrap(ctx context.Context) (*BootstrapResponse, error) {
11	req, err := c.newRequest(ctx, "GET", "/bootstrap", nil)
12	if err != nil {
13		return nil, fmt.Errorf("creating request: %w", err)
14	}
15
16	var resp BootstrapResponse
17	err = c.doJSON(req, &resp)
18	if err != nil {
19		return nil, fmt.Errorf("fetching bootstrap: %w", err)
20	}
21	return &resp, nil
22}
23
24// ResolveCamera finds a camera by ID or name.
25// If the input matches a camera ID exactly, that camera is returned.
26// Otherwise, it searches by name (case-insensitive partial match).
27// Returns ErrCameraNotFound if no match, ErrMultipleCameras if ambiguous.
28func (c *Client) ResolveCamera(ctx context.Context, idOrName string) (*Camera, error) {
29	bootstrap, err := c.Bootstrap(ctx)
30	if err != nil {
31		return nil, err
32	}
33
34	// First try exact ID match
35	for i := range bootstrap.Cameras {
36		if bootstrap.Cameras[i].ID == idOrName {
37			return &bootstrap.Cameras[i], nil
38		}
39	}
40
41	// Then try name match (case-insensitive)
42	var matches []*Camera
43	searchLower := strings.ToLower(idOrName)
44	for i := range bootstrap.Cameras {
45		nameLower := strings.ToLower(bootstrap.Cameras[i].Name)
46		if nameLower == searchLower || strings.Contains(nameLower, searchLower) {
47			matches = append(matches, &bootstrap.Cameras[i])
48		}
49	}
50
51	switch len(matches) {
52	case 0:
53		return nil, fmt.Errorf("%w: %s", ErrCameraNotFound, idOrName)
54	case 1:
55		return matches[0], nil
56	default:
57		names := make([]string, len(matches))
58		for i, cam := range matches {
59			names[i] = fmt.Sprintf("%s (%s)", cam.Name, cam.ID)
60		}
61		return nil, fmt.Errorf("%w: %s matches: %s", ErrMultipleCameras, idOrName, strings.Join(names, ", "))
62	}
63}