/* * Copyright (c) 2018 Jeffrey Walter * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package arlo import ( "fmt" "time" "github.com/pkg/errors" ) /* The methods in this file are all related to Arlo Baby (afaik). They may apply to other camera types that have audio playback or nightlight capabilities. */ /* The follow methods are all related to the audio features of Arlo Baby. */ // SetVolume sets the volume of the audio playback to a level from 0-100. func (c *Camera) SetVolume(volume int) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: SpeakerProperties{ Speaker: VolumeProperties{ Mute: false, Volume: volume, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to set audio volume" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } // Mute mutes the audio playback. func (c *Camera) Mute() (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: SpeakerProperties{ Speaker: VolumeProperties{ Mute: true, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to mute audio" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } // UnMute un-mutes the audio playback. func (c *Camera) UnMute() (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: SpeakerProperties{ Speaker: VolumeProperties{ Mute: false, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to un-mute audio" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } // Play plays an audio track, specified by the track ID, from a given position starting from 0 seconds. func (c *Camera) Play(trackId string, position int) error { // Defaulting to 'hugh little baby', which is a supplied track. Hopefully, the ID is the same for everyone. if trackId == "" { trackId = "2391d620-e491-4412-99f6-e9a40d6046ed" } if position < 0 { position = 0 } payload := EventStreamPayload{ Action: "playTrack", Resource: "audioPlayback/player", PublishResponse: false, Properties: PlayTrackProperties{trackId, position}, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to play audio" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return errors.WithMessage(err, msg) } if err := b.NotifyEventStream(payload, msg); err != nil { return errors.WithMessage(err, msg) } return nil } // Pause pauses audio playback. func (c *Camera) Pause() error { payload := EventStreamPayload{ Action: "pause", Resource: "audioPlayback/player", PublishResponse: false, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to pause audio" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return errors.WithMessage(err, msg) } if err := b.NotifyEventStream(payload, msg); err != nil { return errors.WithMessage(err, msg) } return nil } // Next moves audio playback to the next track. func (c *Camera) Next() error { payload := EventStreamPayload{ Action: "nextTrack", Resource: "audioPlayback/player", PublishResponse: false, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to skip audio" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return errors.WithMessage(err, msg) } if err := b.NotifyEventStream(payload, msg); err != nil { return errors.WithMessage(err, msg) } return nil } // Shuffle toggles the audio play back mode to shuffle or not. func (c *Camera) Shuffle(on bool) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: "audioPlayback/config", PublishResponse: true, Properties: ShuffleProperties{ Config: BaseShuffleProperties{ ShuffleActive: on, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } var msg string if on { msg = "failed to enable shuffle" } else { msg = "failed to disable shuffle" } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } func (c *Camera) Continuous() (response *EventStreamResponse, err error) { return c.SetLoopBackMode("continuous") } func (c *Camera) SingleTrack() (response *EventStreamResponse, err error) { return c.SetLoopBackMode("singleTrack") } func (c *Camera) SetLoopBackMode(loopbackMode string) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: "audioPlayback/config", PublishResponse: true, Properties: LoopbackModeProperties{ Config: BaseLoopbackModeProperties{ LoopbackMode: loopbackMode, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to set loop back mode to %s" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, fmt.Sprintf(msg, loopbackMode)) } return b.makeEventStreamRequest(payload, msg) } func (c *Camera) GetAudioPlayback() (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "get", Resource: "audioPlayback", PublishResponse: false, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to get audio playback" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } func (c *Camera) EnableSleepTimer(sleepTime int64 /* milliseconds */, sleepTimeRel int) (response *EventStreamResponse, err error) { if sleepTime == 0 { sleepTime = 300 + (time.Now().UnixNano() / 1000000) /* milliseconds */ } payload := EventStreamPayload{ Action: "set", Resource: "audioPlayback/config", PublishResponse: true, Properties: SleepTimerProperties{ Config: BaseSleepTimerProperties{ SleepTime: sleepTime, SleepTimeRel: sleepTimeRel, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to enable sleep timer" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } func (c *Camera) DisableSleepTimer(sleepTimeRel int) (response *EventStreamResponse, err error) { if sleepTimeRel == 0 { sleepTimeRel = 300 } payload := EventStreamPayload{ Action: "set", Resource: "audioPlayback/config", PublishResponse: true, Properties: SleepTimerProperties{ Config: BaseSleepTimerProperties{ SleepTime: 0, SleepTimeRel: sleepTimeRel, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to disable sleep timer" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } /* The follow methods are all related to the nightlight features of Arlo Baby. NOTE: The current state is in: cameras[0]["properties"][0]["nightLight"] returned from the basestation.GetAssociatedCamerasState() method. */ func (c *Camera) NightLight(on bool) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: NightLightProperties{ NightLight: BaseNightLightProperties{ Enabled: on, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } var msg string if on { msg = "failed to turn night light on" } else { msg = "failed to turn night light off" } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } func (c *Camera) SetNightLightBrightness(level int) (response *EventStreamResponse, err error) { payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: NightLightProperties{ NightLight: BaseNightLightProperties{ Brightness: level, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to set night light brightness" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } // SetNightLightMode set the night light mode. Valid values are: "rainbow" or "rgb". func (c *Camera) SetNightLightMode(mode string) (response *EventStreamResponse, err error) { msg := "failed to set night light brightness" if mode != "rainbow" && mode != "rgb" { return nil, errors.WithMessage(errors.New("mode can only be \"rainbow\" or \"rgb\""), msg) } payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: NightLightProperties{ NightLight: BaseNightLightProperties{ Mode: mode, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } // SetNightLightColor sets the night light color to the RGB value specified by the three parameters, which have valid values from 0-255. func (c *Camera) SetNightLightColor(red, blue, green int) (response *EventStreamResponse, err error) { // Sanity check; if the values are above or below the allowed limits, set them to their limit. if red < 0 { red = 0 } else if red > 255 { red = 255 } if blue < 0 { blue = 0 } else if blue > 255 { blue = 255 } if green < 0 { green = 0 } else if green > 255 { green = 255 } payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: NightLightProperties{ NightLight: BaseNightLightProperties{ RGB: NightLightRGBProperties{ Red: red, Blue: blue, Green: green, }, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to set night light color" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } func (c *Camera) EnableNightLightTimer(sleepTime int64 /* milliseconds */, sleepTimeRel int) (response *EventStreamResponse, err error) { if sleepTime == 0 { sleepTime = 300 + (time.Now().UnixNano() / 1000000) /* milliseconds */ } payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: NightLightProperties{ NightLight: BaseNightLightProperties{ SleepTime: sleepTime, SleepTimeRel: sleepTimeRel, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to enable night light timer" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) } func (c *Camera) DisableNightLightTimer(sleepTimeRel int) (response *EventStreamResponse, err error) { if sleepTimeRel == 0 { sleepTimeRel = 300 } payload := EventStreamPayload{ Action: "set", Resource: fmt.Sprintf("cameras/%s", c.DeviceId), PublishResponse: true, Properties: NightLightProperties{ NightLight: BaseNightLightProperties{ SleepTime: 0, SleepTimeRel: sleepTimeRel, }, }, From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix), To: c.ParentId, } msg := "failed to disable night light timer" b := c.arlo.Basestations.Find(c.ParentId) if b == nil { err := fmt.Errorf("basestation (%s) not found for camera (%s)", c.ParentId, c.DeviceId) return nil, errors.WithMessage(err, msg) } return b.makeEventStreamRequest(payload, msg) }