2017-12-10 19:17:09 +00:00
|
|
|
package arlo
|
2017-12-09 03:55:38 +00:00
|
|
|
|
2017-12-10 19:17:09 +00:00
|
|
|
import (
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// A Device is the device data, this can be a camera, basestation, arloq, etc.
|
2017-12-09 03:55:38 +00:00
|
|
|
type Device struct {
|
|
|
|
DeviceType string `json:"deviceType"`
|
|
|
|
XCloudId string `json:"xCloudId"`
|
|
|
|
DisplayOrder uint8 `json:"displayOrder"`
|
|
|
|
State string `json:"state"`
|
|
|
|
ModelId string `json:"modelId"`
|
|
|
|
InterfaceVersion string `json:"interfaceVersion"`
|
|
|
|
ParentId string `json:"parentId"`
|
|
|
|
UserId string `json:"userId"`
|
|
|
|
DeviceName string `json:"deviceName"`
|
|
|
|
FirmwareVersion string `json:"firmwareVersion"`
|
|
|
|
MediaObjectCount uint8 `json:"mediaObjectCount"`
|
|
|
|
DateCreated float64 `json:"dateCreated"`
|
|
|
|
Owner Owner `json:"owner"`
|
|
|
|
Properties Properties `json:"properties"`
|
|
|
|
UniqueId string `json:"uniqueId"`
|
|
|
|
LastModified float64 `json:"lastModified"`
|
|
|
|
UserRole string `json:"userRole"`
|
|
|
|
InterfaceSchemaVer string `json:"interfaceSchemaVer"`
|
|
|
|
DeviceId string `json:"deviceId"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Devices is an array of Device objects.
|
|
|
|
type Devices []Device
|
|
|
|
|
2017-12-10 19:17:09 +00:00
|
|
|
// A Basestation is a Device that's not type "camera" (basestation, arloq, arloqs, etc.).
|
|
|
|
// This type is here just for semantics. Some methods explicitly require a device of a certain type.
|
|
|
|
type Basestation Device
|
|
|
|
|
|
|
|
// Basestations is an array of Basestation objects.
|
|
|
|
type Basestations []Basestation
|
|
|
|
|
|
|
|
// A Camera is a Device of type "camera".
|
|
|
|
// This type is here just for semantics. Some methods explicitly require a device of a certain type.
|
|
|
|
type Camera Device
|
|
|
|
|
|
|
|
// Cameras is an array of Camera objects.
|
|
|
|
type Cameras []Camera
|
|
|
|
|
|
|
|
// A DeviceOrder holds a map of device ids and a numeric index. The numeric index is the device order.
|
|
|
|
// Device order is mainly used by the UI to determine which order to show the devices.
|
2017-12-09 03:55:38 +00:00
|
|
|
/*
|
|
|
|
{
|
|
|
|
"devices":{
|
|
|
|
"XXXXXXXXXXXXX":1,
|
|
|
|
"XXXXXXXXXXXXX":2,
|
|
|
|
"XXXXXXXXXXXXX":3
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
type DeviceOrder struct {
|
2017-12-10 19:17:09 +00:00
|
|
|
Devices map[string]int `json:"devices"`
|
2017-12-09 03:55:38 +00:00
|
|
|
}
|
|
|
|
|
2017-12-10 19:17:09 +00:00
|
|
|
// Find returns a device with the device id passed in.
|
2017-12-09 03:55:38 +00:00
|
|
|
func (ds *Devices) Find(deviceId string) *Device {
|
|
|
|
for _, d := range *ds {
|
|
|
|
if d.DeviceId == deviceId {
|
|
|
|
return &d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-12-10 19:17:09 +00:00
|
|
|
// Basestations returns a Basestations object containing all devices that are NOT type "camera".
|
|
|
|
// I did this because some device types, like arloq, don't have a basestation.
|
|
|
|
// So, when interacting with them you must treat them like a basestation and a camera.
|
|
|
|
// Cameras also includes decvices of this type, so you can get the same data there or cast.
|
|
|
|
func (ds *Devices) Basestations() *Basestations {
|
|
|
|
var basestations Basestations
|
2017-12-09 03:55:38 +00:00
|
|
|
for _, d := range *ds {
|
2017-12-10 19:17:09 +00:00
|
|
|
if d.DeviceType != "camera" {
|
|
|
|
basestations = append(basestations, Basestation(d))
|
2017-12-09 03:55:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return &basestations
|
|
|
|
}
|
|
|
|
|
2017-12-10 19:17:09 +00:00
|
|
|
// Cameras returns a Cameras object containing all devices that are of type "camera".
|
|
|
|
// I did this because some device types, like arloq, don't have a basestation.
|
|
|
|
// So, when interacting with them you must treat them like a basestation and a camera.
|
|
|
|
// Basestations also includes decvices of this type, so you can get the same data there or cast.
|
|
|
|
func (ds *Devices) Cameras() *Cameras {
|
|
|
|
var cameras Cameras
|
2017-12-09 03:55:38 +00:00
|
|
|
for _, d := range *ds {
|
|
|
|
if d.DeviceType != "basestation" {
|
2017-12-10 19:17:09 +00:00
|
|
|
cameras = append(cameras, Camera(d))
|
2017-12-09 03:55:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return &cameras
|
|
|
|
}
|
2017-12-10 19:17:09 +00:00
|
|
|
|
|
|
|
// GetDevices returns an array of all devices.
|
|
|
|
// When you call Login, this method is called and all devices are cached in the Arlo object.
|
|
|
|
func (a *Arlo) GetDevices() (*DeviceResponse, error) {
|
|
|
|
|
|
|
|
resp, err := a.client.Get(DevicesUri, nil)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessage(err, "failed to get devices")
|
|
|
|
}
|
|
|
|
|
|
|
|
var deviceResponse DeviceResponse
|
|
|
|
if err := resp.Decode(&deviceResponse); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(deviceResponse.Data) == 0 {
|
|
|
|
return nil, errors.New("no devices found")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &deviceResponse, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateDeviceName sets the name of the given device to the name argument.
|
|
|
|
func (a *Arlo) UpdateDeviceName(d Device, name string) (*Status, error) {
|
|
|
|
|
|
|
|
body := map[string]string{"deviceId": d.DeviceId, "deviceName": name, "parentId": d.ParentId}
|
|
|
|
resp, err := a.client.Put(DeviceRenameUri, body, nil)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessage(err, "failed to update device name")
|
|
|
|
}
|
|
|
|
|
|
|
|
var status Status
|
|
|
|
if err := resp.Decode(&status); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &status, nil
|
|
|
|
|
|
|
|
return nil, errors.New("device not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateDisplayOrder sets the display order according to the order defined in the DeviceOrder given.
|
|
|
|
func (a *Arlo) UpdateDisplayOrder(d DeviceOrder) (*Status, error) {
|
|
|
|
|
|
|
|
resp, err := a.client.Post(DeviceDisplayOrderUri, d, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessage(err, "failed to update display order")
|
|
|
|
}
|
|
|
|
|
|
|
|
var status Status
|
|
|
|
if err := resp.Decode(&status); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &status, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartStream returns a json object containing the rtmps url to the requested video stream.
|
|
|
|
// You will need the to install a library to handle streaming of this protocol: https://pypi.python.org/pypi/python-librtmp
|
|
|
|
//
|
|
|
|
// The request to /users/devices/startStream returns:
|
|
|
|
// NOTE: { "url":"rtmps://vzwow09-z2-prod.vz.netgear.com:80/vzmodulelive?egressToken=b1b4b675_ac03_4182_9844_043e02a44f71&userAgent=web&cameraId=48B4597VD8FF5_1473010750131" }
|
|
|
|
func (a *Arlo) StartStream(c Camera) (*StartStreamResponse, error) {
|
|
|
|
|
|
|
|
var n Notification
|
|
|
|
n.To = c.ParentId
|
|
|
|
n.From = c.UserId
|
|
|
|
n.Resource = "cameras/" + c.DeviceId
|
|
|
|
n.Action = "set"
|
|
|
|
n.PublishResponse = true
|
|
|
|
n.TransId = ""
|
|
|
|
n.Properties.ActivityState = "startUserStream"
|
|
|
|
n.Properties.CameraId = c.DeviceId
|
|
|
|
|
|
|
|
// {"to":camera.get('parentId'),"from":self.user_id+"_web","resource":"cameras/"+camera.get('deviceId'),"action":"set","publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"startUserStream","cameraId":camera.get('deviceId')}}, headers={"xcloudId":camera.get('xCloudId')}
|
|
|
|
resp, err := a.client.Post(DeviceStartStreamUri, n, nil)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithMessage(err, "failed to start stream")
|
|
|
|
}
|
|
|
|
|
|
|
|
var startStreamResponse StartStreamResponse
|
|
|
|
if err := resp.Decode(&startStreamResponse); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &startStreamResponse, nil
|
|
|
|
}
|