package arlo import ( "context" "fmt" ) // 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 type CameraState struct { InterfaceVersion int `json:"interfaceVersion"` SerialNumber string `json:"serialNumber"` BatteryLevel int `json:"batteryLevel"` BatteryTech string `json:"batteryTech"` ChargerTech string `json:"chargerTech"` ChargingState string `json:"chargingState"` ChargeOnly bool `json:"chargeOnly"` ChargeNotificationLedEnable bool `json:"chargeNotificationLedEnable"` AudioMicAGC int `json:"audioMicAGC"` SignalStrength int `json:"signalStrength"` Brightness int `json:"brightness"` Mirror bool `json:"mirror"` Flip bool `json:"flip"` PowerSaveMode int `json:"powerSaveMode"` Zoom struct { Topleftx int `json:"topleftx"` Toplefty int `json:"toplefty"` Bottomrightx int `json:"bottomrightx"` Bottomrighty int `json:"bottomrighty"` } `json:"zoom"` Mic struct { Mute bool `json:"mute"` Volume int `json:"volume"` } `json:"mic"` Speaker struct { Mute bool `json:"mute"` Volume int `json:"volume"` } `json:"speaker"` StreamingMode string `json:"streamingMode"` ContinuousStreamState string `json:"continuousStreamState"` Motion struct { Sensitivity int `json:"sensitivity"` Zones []interface{} `json:"zones"` } `json:"motion"` Resolution struct { Width int `json:"width"` Height int `json:"height"` } `json:"resolution"` IdleLedEnable bool `json:"idleLedEnable"` PrivacyActive bool `json:"privacyActive"` StandbyActive bool `json:"standbyActive"` SetupActive bool `json:"setupActive"` ConnectionState string `json:"connectionState"` ActivityState string `json:"activityState"` SwVersion string `json:"swVersion"` HwVersion string `json:"hwVersion"` ModelID string `json:"modelId"` MotionSetupModeEnabled bool `json:"motionSetupModeEnabled"` MotionSetupModeSensitivity int `json:"motionSetupModeSensitivity"` MotionDetected bool `json:"motionDetected"` AudioDetected bool `json:"audioDetected"` HasStreamed bool `json:"hasStreamed"` LocalRecordingActive bool `json:"localRecordingActive"` OlsonTimeZone string `json:"olsonTimeZone"` Name string `json:"name"` NightVisionMode int `json:"nightVisionMode"` VideoMode string `json:"videoMode"` Hdr string `json:"hdr"` UpdateAvailable interface{} `json:"updateAvailable"` BlockNotifications struct { Block bool `json:"block"` Duration int `json:"duration"` EndTime int `json:"endTime"` } `json:"blockNotifications"` BestLocalLiveStreaming string `json:"bestLocalLiveStreaming"` } // Cameras is a slice of Camera objects. type Cameras []*Camera // Find returns a camera with the device id passed in. func (cs *Cameras) Find(deviceId string) *Camera { for _, c := range *cs { if c.DeviceId == deviceId { return c } } return nil } func (c *Camera) On(ctx context.Context) error { b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } err := b.makeRequest(ctx, "set", fmt.Sprintf("cameras/%s", c.DeviceId), true, CameraProperties{ PrivacyActive: false, }, nil) if err != nil { return err } return nil } // On turns a camera off; meaning it won't detect and record events. func (c *Camera) Off(ctx context.Context) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: CameraProperties{ PrivacyActive: true, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return nil, fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } return b.makeEventStreamRequest(ctx, payload) } // SetBrightness sets the camera brightness. // NOTE: Brightness is between -2 and 2 in increments of 1 (-2, -1, 0, 1, 2). // Setting it to an invalid value has no effect. func (c *Camera) SetBrightness(ctx context.Context, brightness int) (response *EventStreamResponse, err error) { // Sanity check; if the values are above or below the allowed limits, set them to their limit. if brightness < -2 { brightness = -2 } else if brightness > 2 { brightness = 2 } payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: CameraProperties{ Brightness: brightness, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return nil, fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } return b.makeEventStreamRequest(ctx, payload) } func (c *Camera) EnableMotionAlerts(ctx context.Context, sensitivity int, zones []string) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: MotionDetectionProperties{ BaseDetectionProperties: BaseDetectionProperties{ Armed: true, Sensitivity: sensitivity, Zones: zones, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return nil, fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } return b.makeEventStreamRequest(ctx, payload) } func (c *Camera) DisableMotionAlerts(ctx context.Context, sensitivity int, zones []string) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: MotionDetectionProperties{ BaseDetectionProperties: BaseDetectionProperties{ Armed: false, Sensitivity: sensitivity, Zones: zones, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return nil, fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } return b.makeEventStreamRequest(ctx, payload) } func (c *Camera) EnableAudioAlerts(ctx context.Context, sensitivity int) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: AudioDetectionProperties{ BaseDetectionProperties: BaseDetectionProperties{ Armed: true, Sensitivity: sensitivity, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return nil, fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } return b.makeEventStreamRequest(ctx, payload) } func (c *Camera) DisableAudioAlerts(ctx context.Context, sensitivity int) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: AudioDetectionProperties{ BaseDetectionProperties: BaseDetectionProperties{ Armed: false, Sensitivity: sensitivity, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return nil, fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } return b.makeEventStreamRequest(ctx, payload) } // action: disabled OR recordSnapshot OR recordVideo func (c *Camera) SetAlertNotificationMethods(ctx context.Context, action string, email, push bool) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: EventActionProperties{ BaseEventActionProperties: BaseEventActionProperties{ ActionType: action, StopType: "timeout", Timeout: 15, EmailNotification: EmailNotification{ Enabled: email, EmailList: []string{"__OWNER_EMAIL__"}, PushNotification: push, }, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { return nil, fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) } return b.makeEventStreamRequest(ctx, payload) }