package arlo import ( "context" "fmt" "sync" "time" "github.com/go-resty/resty/v2" ) type Arlo struct { user string pass string client *resty.Client Account Account Basestations Basestations Cameras Cameras rwmutex sync.RWMutex } func NewArlo() (arlo *Arlo) { c := resty.New(). SetHostURL(BaseUrl). SetTimeout(30 * time.Second) return &Arlo{ client: c, } } func (a *Arlo) get(url string, result interface{}) error { var errorResponse ErrorResponse resp, err := a.client.R(). SetResult(result). SetError(&errorResponse). Get(url) if err != nil { return err } if resp.IsError() { return fmt.Errorf(errorResponse.Reason) } return nil } func (a *Arlo) post(url string, body interface{}, result interface{}, xcloudId string) error { var errorResponse ErrorResponse request := a.client.R(). SetBody(body). SetResult(result). SetError(&errorResponse) if xcloudId != "" { request.SetHeader("xcloudId", xcloudId) } resp, err := request.Post(url) if err != nil { return err } if resp.IsError() { return fmt.Errorf(errorResponse.Reason) } return nil } func (a *Arlo) put(url string, result interface{}, xcloudId string) error { var errorResponse ErrorResponse request := a.client.R(). SetResult(result). SetError(&errorResponse) if xcloudId != "" { request.SetHeader("xcloudId", xcloudId) } resp, err := request.Put(url) if err != nil { return err } if resp.IsError() { return fmt.Errorf(errorResponse.Reason) } return nil } func (a *Arlo) Login(ctx context.Context, user string, pass string) error { var loginResponse LoginResponse err := a.post(LoginV2Uri, map[string]string{ "email": user, "password": pass, }, &loginResponse, "") if err != nil { return fmt.Errorf("posting login request: %v", err) } if !loginResponse.Success { return fmt.Errorf("no success but no error") } a.client.SetHeader("Authorization", loginResponse.Data.Token) a.Account = loginResponse.Data if _, err := a.GetDevices(ctx); err != nil { return fmt.Errorf("getting devices: %v", err) } return nil } func (a *Arlo) Logout() error { var response BaseResponse err := a.put(LogoutUri, &response, "") if err != nil { return err } if !response.Success { return fmt.Errorf("no success but no error") } return nil } func (a *Arlo) GetSession() (*Session, error) { var response SessionResponse err := a.get(SessionUri, &response) if err != nil { return nil, err } if !response.Success { return nil, fmt.Errorf("no success but no error") } return &response.Data, nil } func (a *Arlo) GetDevices(ctx context.Context) (*Devices, error) { var response DeviceResponse err := a.get(fmt.Sprintf(DevicesUri, time.Now().Format("20060102")), &response) if err != nil { return nil, err } if !response.Success { return nil, fmt.Errorf("no success but no error") } if len(response.Data) == 0 { return nil, fmt.Errorf("no device found") } // Cache a pointer to the arlo object with each device. for i := range response.Data { response.Data[i].arlo = a } // Disconnect all of the basestations from the EventStream. for _, basestation := range a.Basestations { if err := basestation.Disconnect(); err != nil { return nil, fmt.Errorf("disconnecting device %s: %v", basestation.DeviceName, err) } } a.rwmutex.Lock() // Cache the devices as their respective types. a.Cameras = response.Data.GetCameras() a.Basestations = response.Data.GetBasestations() a.rwmutex.Unlock() // subscribe each basestation to the EventStream. for _, basestation := range a.Basestations { if err := basestation.Subscribe(ctx); err != nil { return nil, fmt.Errorf("subscribing device %s: %v", basestation.DeviceName, err) } } return &response.Data, nil } // GetProfile returns the user profile for the currently logged in user. func (a *Arlo) GetProfile() (*UserProfile, error) { var response UserProfileResponse err := a.get(ProfileUri, &response) if err != nil { return nil, err } if !response.Success { return nil, fmt.Errorf("no success but no error") } return &response.Data, nil }