More cleanup. Added copyright notice.
This commit is contained in:
parent
8213d70258
commit
648de10f10
6
Gopkg.lock
generated
6
Gopkg.lock
generated
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca"
|
branch = "master"
|
||||||
|
digest = "1:4a28080faacdaabfe54a78ba0bfb41468744cbcda6af15748b917ca725d3749c"
|
||||||
name = "github.com/pkg/errors"
|
name = "github.com/pkg/errors"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = ""
|
pruneopts = ""
|
||||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
revision = "c059e472caf75dbe73903f6521a20abac245b17f"
|
||||||
version = "v0.8.0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
51
arlo.go
51
arlo.go
@ -1,6 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jeffreydwalter/arlo-golang/internal/request"
|
"github.com/jeffreydwalter/arlo-golang/internal/request"
|
||||||
@ -20,13 +37,14 @@ type Arlo struct {
|
|||||||
|
|
||||||
func newArlo(user string, pass string) (arlo *Arlo) {
|
func newArlo(user string, pass string) (arlo *Arlo) {
|
||||||
|
|
||||||
c, _ := request.NewClient(BaseUrl)
|
|
||||||
|
|
||||||
// Add important headers.
|
// Add important headers.
|
||||||
c.BaseHttpHeader.Add("DNT", "1")
|
baseHeaders := make(http.Header)
|
||||||
c.BaseHttpHeader.Add("schemaVersion", "1")
|
baseHeaders.Add("DNT", "1")
|
||||||
c.BaseHttpHeader.Add("Host", "arlo.netgear.com")
|
baseHeaders.Add("schemaVersion", "1")
|
||||||
c.BaseHttpHeader.Add("Referer", "https://arlo.netgear.com/")
|
baseHeaders.Add("Host", "arlo.netgear.com")
|
||||||
|
baseHeaders.Add("Referer", "https://arlo.netgear.com/")
|
||||||
|
|
||||||
|
c, _ := request.NewClient(BaseUrl, baseHeaders)
|
||||||
|
|
||||||
return &Arlo{
|
return &Arlo{
|
||||||
user: user,
|
user: user,
|
||||||
@ -40,8 +58,8 @@ func Login(user string, pass string) (arlo *Arlo, err error) {
|
|||||||
|
|
||||||
body := map[string]string{"email": arlo.user, "password": arlo.pass}
|
body := map[string]string{"email": arlo.user, "password": arlo.pass}
|
||||||
resp, err := arlo.post(LoginUri, "", body, nil)
|
resp, err := arlo.post(LoginUri, "", body, nil)
|
||||||
if err := checkHttpRequest(resp, err, "login request failed"); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.WithMessage(err, "failed to login")
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
@ -52,7 +70,7 @@ func Login(user string, pass string) (arlo *Arlo, err error) {
|
|||||||
|
|
||||||
if loginResponse.Success {
|
if loginResponse.Success {
|
||||||
// Cache the auth token.
|
// Cache the auth token.
|
||||||
arlo.client.BaseHttpHeader.Add("Authorization", loginResponse.Data.Token)
|
arlo.client.AddHeader("Authorization", loginResponse.Data.Token)
|
||||||
|
|
||||||
// Save the account info with the arlo struct.
|
// Save the account info with the arlo struct.
|
||||||
arlo.Account = loginResponse.Data
|
arlo.Account = loginResponse.Data
|
||||||
@ -75,10 +93,10 @@ func (a *Arlo) Logout() error {
|
|||||||
|
|
||||||
// GetDevices returns an array of all devices.
|
// GetDevices returns an array of all devices.
|
||||||
// When you call Login, this method is called and all devices are cached in the arlo object.
|
// When you call Login, this method is called and all devices are cached in the arlo object.
|
||||||
func (a *Arlo) GetDevices() (devices Devices, err error) {
|
func (a *Arlo) GetDevices() (devices *Devices, err error) {
|
||||||
resp, err := a.get(DevicesUri, "", nil)
|
resp, err := a.get(DevicesUri, "", nil)
|
||||||
if err := checkHttpRequest(resp, err, "failed to get devices"); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.WithMessage(err, "failed to get devices")
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
@ -95,11 +113,12 @@ func (a *Arlo) GetDevices() (devices Devices, err error) {
|
|||||||
return nil, errors.New("no devices found")
|
return nil, errors.New("no devices found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache a pointer to the arlo object with each device.
|
||||||
for i := range deviceResponse.Data {
|
for i := range deviceResponse.Data {
|
||||||
deviceResponse.Data[i].arlo = a
|
deviceResponse.Data[i].arlo = a
|
||||||
}
|
}
|
||||||
|
|
||||||
// disconnect all of the basestations from the EventStream.
|
// Disconnect all of the basestations from the EventStream.
|
||||||
for i := range a.Basestations {
|
for i := range a.Basestations {
|
||||||
if err := a.Basestations[i].Disconnect(); err != nil {
|
if err := a.Basestations[i].Disconnect(); err != nil {
|
||||||
return nil, errors.WithMessage(err, "failed to get devices")
|
return nil, errors.WithMessage(err, "failed to get devices")
|
||||||
@ -108,8 +127,8 @@ func (a *Arlo) GetDevices() (devices Devices, err error) {
|
|||||||
|
|
||||||
a.rwmutex.Lock()
|
a.rwmutex.Lock()
|
||||||
// Cache the devices as their respective types.
|
// Cache the devices as their respective types.
|
||||||
a.Cameras = deviceResponse.Data.GetCameras()
|
a.Cameras = *deviceResponse.Data.GetCameras()
|
||||||
a.Basestations = deviceResponse.Data.GetBasestations()
|
a.Basestations = *deviceResponse.Data.GetBasestations()
|
||||||
a.rwmutex.Unlock()
|
a.rwmutex.Unlock()
|
||||||
|
|
||||||
// subscribe each basestation to the EventStream.
|
// subscribe each basestation to the EventStream.
|
||||||
@ -119,7 +138,7 @@ func (a *Arlo) GetDevices() (devices Devices, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return deviceResponse.Data, nil
|
return &deviceResponse.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateDisplayOrder sets the display order according to the order defined in the DeviceOrder given.
|
// UpdateDisplayOrder sets the display order according to the order defined in the DeviceOrder given.
|
||||||
|
16
arlo_test.go
16
arlo_test.go
@ -1 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
16
arlobaby.go
16
arlobaby.go
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -17,7 +33,7 @@ type Basestation struct {
|
|||||||
eventStream *eventStream
|
eventStream *eventStream
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basestations is an array of Basestation objects.
|
// Basestations is a slice of Basestation objects.
|
||||||
type Basestations []Basestation
|
type Basestations []Basestation
|
||||||
|
|
||||||
// Find returns a basestation with the device id passed in.
|
// Find returns a basestation with the device id passed in.
|
||||||
|
173
camera.go
173
camera.go
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -11,7 +27,7 @@ import (
|
|||||||
// This type is here just for semantics. Some methods explicitly require a device of a certain type.
|
// This type is here just for semantics. Some methods explicitly require a device of a certain type.
|
||||||
type Camera Device
|
type Camera Device
|
||||||
|
|
||||||
// Cameras is an array of Camera objects.
|
// Cameras is a slice of Camera objects.
|
||||||
type Cameras []Camera
|
type Cameras []Camera
|
||||||
|
|
||||||
// Find returns a camera with the device id passed in.
|
// Find returns a camera with the device id passed in.
|
||||||
@ -103,83 +119,6 @@ func (c *Camera) SetBrightness(brightness int) (response *EventStreamResponse, e
|
|||||||
return b.makeEventStreamRequest(payload, msg)
|
return b.makeEventStreamRequest(payload, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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":"rtsp://vzwow09-z2-prod.vz.netgear.com:80/vzmodulelive?egressToken=b1b4b675_ac03_4182_9844_043e02a44f71&userAgent=web&cameraId=48B4597VD8FF5_1473010750131" }
|
|
||||||
func (c *Camera) StartStream() (response *StreamResponse, err error) {
|
|
||||||
payload := EventStreamPayload{
|
|
||||||
Action: "set",
|
|
||||||
Resource: fmt.Sprintf("cameras/%s", c.DeviceId),
|
|
||||||
PublishResponse: true,
|
|
||||||
Properties: map[string]string{
|
|
||||||
"activityState": "startUserStream",
|
|
||||||
"cameraId": c.DeviceId,
|
|
||||||
},
|
|
||||||
TransId: genTransId(),
|
|
||||||
From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix),
|
|
||||||
To: c.ParentId,
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := "failed to start stream"
|
|
||||||
|
|
||||||
resp, err := c.arlo.post(DeviceStartStreamUri, c.XCloudId, payload, nil)
|
|
||||||
if err := checkHttpRequest(resp, err, msg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if err := resp.Decode(response); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !response.Success {
|
|
||||||
return nil, errors.WithMessage(errors.New("status was false"), msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
response.Data.Url = strings.Replace(response.Data.Url, "rtsp://", "rtsps://", 1)
|
|
||||||
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TakeSnapshot causes the camera to record a snapshot.
|
|
||||||
func (c *Camera) TakeSnapshot() (response *StreamResponse, err error) {
|
|
||||||
msg := "failed to take snapshot"
|
|
||||||
|
|
||||||
response, err = c.StartStream()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.WithMessage(err, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
body := map[string]string{"deviceId": c.DeviceId, "parentId": c.ParentId, "xcloudId": c.XCloudId, "olsonTimeZone": c.Properties.OlsonTimeZone}
|
|
||||||
resp, err := c.arlo.post(DeviceTakeSnapshotUri, c.XCloudId, body, nil)
|
|
||||||
if err := checkRequest(resp, err, "failed to update device name"); err != nil {
|
|
||||||
return nil, errors.WithMessage(err, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartRecording causes the camera to start recording and returns a url that you must start reading from using ffmpeg
|
|
||||||
// or something similar.
|
|
||||||
func (c *Camera) StartRecording() (response *StreamResponse, err error) {
|
|
||||||
msg := "failed to start recording"
|
|
||||||
|
|
||||||
response, err = c.StartStream()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.WithMessage(err, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
body := map[string]string{"deviceId": c.DeviceId, "parentId": c.ParentId, "xcloudId": c.XCloudId, "olsonTimeZone": c.Properties.OlsonTimeZone}
|
|
||||||
resp, err := c.arlo.post(DeviceStartRecordUri, c.XCloudId, body, nil)
|
|
||||||
if err := checkRequest(resp, err, "failed to update device name"); err != nil {
|
|
||||||
return nil, errors.WithMessage(err, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Camera) EnableMotionAlerts(sensitivity int, zones []string) (response *EventStreamResponse, err error) {
|
func (c *Camera) EnableMotionAlerts(sensitivity int, zones []string) (response *EventStreamResponse, err error) {
|
||||||
payload := EventStreamPayload{
|
payload := EventStreamPayload{
|
||||||
Action: "set",
|
Action: "set",
|
||||||
@ -321,3 +260,81 @@ func (c *Camera) SetAlertNotificationMethods(action string, email, push bool) (r
|
|||||||
}
|
}
|
||||||
return b.makeEventStreamRequest(payload, msg)
|
return b.makeEventStreamRequest(payload, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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":"rtsp://vzwow09-z2-prod.vz.netgear.com:80/vzmodulelive?egressToken=b1b4b675_ac03_4182_9844_043e02a44f71&userAgent=web&cameraId=48B4597VD8FF5_1473010750131" }
|
||||||
|
func (c *Camera) StartStream() (url string, err error) {
|
||||||
|
payload := EventStreamPayload{
|
||||||
|
Action: "set",
|
||||||
|
Resource: fmt.Sprintf("cameras/%s", c.DeviceId),
|
||||||
|
PublishResponse: true,
|
||||||
|
Properties: map[string]string{
|
||||||
|
"activityState": "startUserStream",
|
||||||
|
"cameraId": c.DeviceId,
|
||||||
|
},
|
||||||
|
TransId: genTransId(),
|
||||||
|
From: fmt.Sprintf("%s_%s", c.UserId, TransIdPrefix),
|
||||||
|
To: c.ParentId,
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := "failed to start stream"
|
||||||
|
|
||||||
|
resp, err := c.arlo.post(DeviceStartStreamUri, c.XCloudId, payload, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
response := new(StreamResponse)
|
||||||
|
if err := resp.Decode(response); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !response.Success {
|
||||||
|
return "", errors.WithMessage(errors.New("status was false"), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
response.URL = strings.Replace(response.URL, "rtsp://", "rtsps://", 1)
|
||||||
|
|
||||||
|
return response.URL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TakeSnapshot causes the camera to record a snapshot.
|
||||||
|
func (c *Camera) TakeSnapshot() (url string, err error) {
|
||||||
|
msg := "failed to take snapshot"
|
||||||
|
|
||||||
|
url, err = c.StartStream()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := map[string]string{"deviceId": c.DeviceId, "parentId": c.ParentId, "xcloudId": c.XCloudId, "olsonTimeZone": c.Properties.OlsonTimeZone}
|
||||||
|
resp, err := c.arlo.post(DeviceTakeSnapshotUri, c.XCloudId, body, nil)
|
||||||
|
if err := checkRequest(resp, err, "failed to update device name"); err != nil {
|
||||||
|
return "", errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return url, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartRecording causes the camera to start recording and returns a url that you must start reading from using ffmpeg
|
||||||
|
// or something similar.
|
||||||
|
func (c *Camera) StartRecording() (url string, err error) {
|
||||||
|
msg := "failed to start recording"
|
||||||
|
|
||||||
|
url, err = c.StartStream()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := map[string]string{"deviceId": c.DeviceId, "parentId": c.ParentId, "xcloudId": c.XCloudId, "olsonTimeZone": c.Properties.OlsonTimeZone}
|
||||||
|
resp, err := c.arlo.post(DeviceStartRecordUri, c.XCloudId, body, nil)
|
||||||
|
if err := checkRequest(resp, err, "failed to update device name"); err != nil {
|
||||||
|
return "", errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return url, nil
|
||||||
|
}
|
||||||
|
20
const.go
20
const.go
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -7,7 +23,6 @@ const (
|
|||||||
SubscribeUri = "/client/subscribe?token=%s"
|
SubscribeUri = "/client/subscribe?token=%s"
|
||||||
UnsubscribeUri = "/client/unsubscribe"
|
UnsubscribeUri = "/client/unsubscribe"
|
||||||
NotifyUri = "/users/devices/notify/%s"
|
NotifyUri = "/users/devices/notify/%s"
|
||||||
ResetUri = "/users/library/reset"
|
|
||||||
ServiceLevelUri = "/users/serviceLevel"
|
ServiceLevelUri = "/users/serviceLevel"
|
||||||
OffersUri = "/users/payment/offers"
|
OffersUri = "/users/payment/offers"
|
||||||
UserProfileUri = "/users/profile"
|
UserProfileUri = "/users/profile"
|
||||||
@ -18,8 +33,9 @@ const (
|
|||||||
UserLocationsUri = "/users/locations"
|
UserLocationsUri = "/users/locations"
|
||||||
UserLocationUri = "/users/locations/%s"
|
UserLocationUri = "/users/locations/%s"
|
||||||
LibraryUri = "/users/library"
|
LibraryUri = "/users/library"
|
||||||
LibraryRecycleUri = "/users/library/recycle"
|
|
||||||
LibraryMetadataUri = "/users/library/metadata"
|
LibraryMetadataUri = "/users/library/metadata"
|
||||||
|
LibraryRecycleUri = "/users/library/recycle"
|
||||||
|
LibraryResetUri = "/users/library/reset"
|
||||||
DevicesUri = "/users/devices"
|
DevicesUri = "/users/devices"
|
||||||
DeviceRenameUri = "/users/devices/renameDevice"
|
DeviceRenameUri = "/users/devices/renameDevice"
|
||||||
DeviceDisplayOrderUri = "/users/devices/displayOrder"
|
DeviceDisplayOrderUri = "/users/devices/displayOrder"
|
||||||
|
34
devices.go
34
devices.go
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
// A Device is the device data, this can be a camera, basestation, arloq, etc.
|
// A Device is the device data, this can be a camera, basestation, arloq, etc.
|
||||||
@ -42,7 +58,7 @@ type Device struct {
|
|||||||
XCloudId string `json:"xCloudId"`
|
XCloudId string `json:"xCloudId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Devices is an array of Device objects.
|
// Devices is a slice of Device objects.
|
||||||
type Devices []Device
|
type Devices []Device
|
||||||
|
|
||||||
// A DeviceOrder holds a map of device ids and a numeric index. The numeric index is the device order.
|
// A DeviceOrder holds a map of device ids and a numeric index. The numeric index is the device order.
|
||||||
@ -70,9 +86,9 @@ func (ds *Devices) Find(deviceId string) *Device {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *Devices) FindCameras(basestationId string) Cameras {
|
func (ds Devices) FindCameras(basestationId string) Cameras {
|
||||||
cs := new(Cameras)
|
cs := new(Cameras)
|
||||||
for _, d := range *ds {
|
for _, d := range ds {
|
||||||
if d.ParentId == basestationId {
|
if d.ParentId == basestationId {
|
||||||
*cs = append(*cs, Camera(d))
|
*cs = append(*cs, Camera(d))
|
||||||
}
|
}
|
||||||
@ -93,11 +109,11 @@ func (d Device) IsCamera() bool {
|
|||||||
// I did this because some device types, like arloq, don't have a basestation.
|
// 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.
|
// So, when interacting with them you must treat them like a basestation and a camera.
|
||||||
// Cameras also includes devices of this type, so you can get the same data there or cast.
|
// Cameras also includes devices of this type, so you can get the same data there or cast.
|
||||||
func (ds Devices) GetBasestations() Basestations {
|
func (ds Devices) GetBasestations() *Basestations {
|
||||||
var basestations Basestations
|
basestations := new(Basestations)
|
||||||
for _, d := range ds {
|
for _, d := range ds {
|
||||||
if d.IsBasestation() || !d.IsCamera() {
|
if d.IsBasestation() || !d.IsCamera() {
|
||||||
basestations = append(basestations, Basestation{Device: d})
|
*basestations = append(*basestations, Basestation{Device: d})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return basestations
|
return basestations
|
||||||
@ -107,11 +123,11 @@ func (ds Devices) GetBasestations() Basestations {
|
|||||||
// I did this because some device types, like arloq, don't have a basestation.
|
// 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.
|
// So, when interacting with them you must treat them like a basestation and a camera.
|
||||||
// Basestations also includes devices of this type, so you can get the same data there or cast.
|
// Basestations also includes devices of this type, so you can get the same data there or cast.
|
||||||
func (ds Devices) GetCameras() Cameras {
|
func (ds Devices) GetCameras() *Cameras {
|
||||||
var cameras Cameras
|
cameras := new(Cameras)
|
||||||
for _, d := range ds {
|
for _, d := range ds {
|
||||||
if d.IsCamera() || !d.IsBasestation() {
|
if d.IsCamera() || !d.IsBasestation() {
|
||||||
cameras = append(cameras, Camera(d))
|
*cameras = append(*cameras, Camera(d))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cameras
|
return cameras
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -74,7 +90,7 @@ func (e *eventStream) listen() (connected chan bool) {
|
|||||||
/*
|
/*
|
||||||
fmt.Println("Got event message.")
|
fmt.Println("Got event message.")
|
||||||
fmt.Printf("EVENT: %s\n", event.Event)
|
fmt.Printf("EVENT: %s\n", event.Event)
|
||||||
fmt.Printf("DATA: %s\n", event.Data)
|
fmt.Printf("DATA: %s\n", event.URL)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if event.Data != nil {
|
if event.Data != nil {
|
||||||
|
@ -1,12 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 request
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
@ -14,12 +32,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
BaseURL *url.URL
|
BaseURL *url.URL
|
||||||
BaseHttpHeader *http.Header
|
BaseHeaders *http.Header
|
||||||
HttpClient *http.Client
|
HttpClient *http.Client
|
||||||
|
rwmutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(baseurl string) (*Client, error) {
|
func NewClient(baseURL string, baseHeaders http.Header) (*Client, error) {
|
||||||
var err error
|
var err error
|
||||||
var jar *cookiejar.Jar
|
var jar *cookiejar.Jar
|
||||||
|
|
||||||
@ -30,7 +49,7 @@ func NewClient(baseurl string) (*Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u *url.URL
|
var u *url.URL
|
||||||
if u, err = url.Parse(baseurl); err != nil {
|
if u, err = url.Parse(baseURL); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create client object")
|
return nil, errors.Wrap(err, "failed to create client object")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,12 +59,18 @@ func NewClient(baseurl string) (*Client, error) {
|
|||||||
header.Set("Accept", "application/json")
|
header.Set("Accept", "application/json")
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
BaseURL: u,
|
BaseURL: u,
|
||||||
BaseHttpHeader: &header,
|
BaseHeaders: &header,
|
||||||
HttpClient: &http.Client{Jar: jar},
|
HttpClient: &http.Client{Jar: jar},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) AddHeader(key, value string) {
|
||||||
|
c.rwmutex.Lock()
|
||||||
|
c.BaseHeaders.Set(key, value)
|
||||||
|
c.rwmutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Get(uri string, header http.Header) (*Response, error) {
|
func (c *Client) Get(uri string, header http.Header) (*Response, error) {
|
||||||
req, err := c.newRequest("GET", uri, nil, header)
|
req, err := c.newRequest("GET", uri, nil, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -80,7 +105,7 @@ func (c *Client) newRequest(method string, uri string, body interface{}, header
|
|||||||
return nil, errors.Wrap(err, "failed to create request object")
|
return nil, errors.Wrap(err, "failed to create request object")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// log.Printf("\n\nBODY (%s): %s\n\n", uri, buf)
|
log.Printf("\n\nBODY (%s): %s\n\n", uri, buf)
|
||||||
|
|
||||||
u := c.BaseURL.String() + uri
|
u := c.BaseURL.String() + uri
|
||||||
req, err := http.NewRequest(method, u, buf)
|
req, err := http.NewRequest(method, u, buf)
|
||||||
@ -88,7 +113,11 @@ func (c *Client) newRequest(method string, uri string, body interface{}, header
|
|||||||
return nil, errors.Wrap(err, "failed to create request object")
|
return nil, errors.Wrap(err, "failed to create request object")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range *c.BaseHttpHeader {
|
c.rwmutex.RLock()
|
||||||
|
baseHeaders := *c.BaseHeaders
|
||||||
|
c.rwmutex.RUnlock()
|
||||||
|
|
||||||
|
for k, v := range baseHeaders {
|
||||||
for _, h := range v {
|
for _, h := range v {
|
||||||
//log.Printf("Adding header (%s): (%s - %s)\n\n", u, k, h)
|
//log.Printf("Adding header (%s): (%s - %s)\n\n", u, k, h)
|
||||||
req.Header.Set(k, h)
|
req.Header.Set(k, h)
|
||||||
@ -116,8 +145,8 @@ func (c *Client) newResponse(resp *http.Response) (*Response, error) {
|
|||||||
|
|
||||||
func (c *Client) do(req *Request) (*Response, error) {
|
func (c *Client) do(req *Request) (*Response, error) {
|
||||||
|
|
||||||
//log.Printf("\n\nCOOKIES (%s): %v\n\n", req.URL, c.HttpClient.Jar.Cookies(req.URL))
|
log.Printf("\n\nCOOKIES (%s): %v\n\n", req.URL, c.HttpClient.Jar.Cookies(req.URL))
|
||||||
//log.Printf("\n\nHEADERS (%s): %v\n\n", req.URL, req.Header)
|
log.Printf("\n\nHEADERS (%s): %v\n\n", req.URL, req.Header)
|
||||||
|
|
||||||
resp, err := c.HttpClient.Do(&req.Request)
|
resp, err := c.HttpClient.Do(&req.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 request
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 request
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
68
library.go
68
library.go
@ -1,7 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LibraryMetaData is the library meta data.
|
// LibraryMetaData is the library meta data.
|
||||||
@ -30,38 +49,53 @@ type Recording struct {
|
|||||||
UtcCreatedDate int64 `json:"utcCreatedDate"`
|
UtcCreatedDate int64 `json:"utcCreatedDate"`
|
||||||
CurrentState string `json:"currentState"`
|
CurrentState string `json:"currentState"`
|
||||||
MediaDuration string `json:"mediaDuration"`
|
MediaDuration string `json:"mediaDuration"`
|
||||||
|
UniqueId string `json:"uniqueId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Library []Recording
|
type Library []Recording
|
||||||
|
|
||||||
func (a *Arlo) GetLibraryMetaData(fromDate, toDate time.Time) (response *LibraryMetaDataResponse, err error) {
|
func (a *Arlo) GetLibraryMetaData(fromDate, toDate time.Time) (libraryMetaData *LibraryMetaData, err error) {
|
||||||
|
msg := "failed to get library metadata"
|
||||||
|
|
||||||
body := map[string]string{"dateFrom": fromDate.Format("20060102"), "dateTo": toDate.Format("20060102")}
|
body := map[string]string{"dateFrom": fromDate.Format("20060102"), "dateTo": toDate.Format("20060102")}
|
||||||
resp, err := a.post(LibraryMetadataUri, "", body, nil)
|
resp, err := a.post(LibraryMetadataUri, "", body, nil)
|
||||||
if err := checkHttpRequest(resp, err, "failed to get library metadata"); err != nil {
|
if err != nil {
|
||||||
|
return nil, errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
response := new(LibraryMetaDataResponse)
|
||||||
|
if err := resp.Decode(&response); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := resp.Decode(response); err != nil {
|
if !response.Success {
|
||||||
return nil, err
|
return nil, errors.New(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return &response.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Arlo) GetLibrary(fromDate, toDate time.Time) (response *LibraryResponse, err error) {
|
func (a *Arlo) GetLibrary(fromDate, toDate time.Time) (library *Library, err error) {
|
||||||
|
msg := "failed to get library"
|
||||||
|
|
||||||
body := map[string]string{"dateFrom": fromDate.Format("20060102"), "dateTo": toDate.Format("20060102")}
|
body := map[string]string{"dateFrom": fromDate.Format("20060102"), "dateTo": toDate.Format("20060102")}
|
||||||
resp, err := a.post(LibraryUri, "", body, nil)
|
resp, err := a.post(LibraryUri, "", body, nil)
|
||||||
if err := checkHttpRequest(resp, err, "failed to get library"); err != nil {
|
if err != nil {
|
||||||
|
return nil, errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
response := new(LibraryResponse)
|
||||||
|
if err := resp.Decode(&response); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := resp.Decode(response); err != nil {
|
if !response.Success {
|
||||||
return nil, err
|
return nil, errors.New(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return &response.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -71,9 +105,9 @@ func (a *Arlo) GetLibrary(fromDate, toDate time.Time) (response *LibraryResponse
|
|||||||
|
|
||||||
NOTE: {"data": [{"createdDate": r.CreatedDate, "utcCreatedDate": r.UtcCreatedDate, "deviceId": r.DeviceId}]} is all that's really required.
|
NOTE: {"data": [{"createdDate": r.CreatedDate, "utcCreatedDate": r.UtcCreatedDate, "deviceId": r.DeviceId}]} is all that's really required.
|
||||||
*/
|
*/
|
||||||
func (a *Arlo) DeleteRecording(r Recording) error {
|
func (a *Arlo) DeleteRecording(r *Recording) error {
|
||||||
|
|
||||||
body := map[string]Library{"data": {r}}
|
body := map[string]Library{"data": {*r}}
|
||||||
resp, err := a.post(LibraryRecycleUri, "", body, nil)
|
resp, err := a.post(LibraryRecycleUri, "", body, nil)
|
||||||
return checkRequest(resp, err, "failed to delete recording")
|
return checkRequest(resp, err, "failed to delete recording")
|
||||||
}
|
}
|
||||||
@ -85,9 +119,11 @@ func (a *Arlo) DeleteRecording(r Recording) error {
|
|||||||
|
|
||||||
NOTE: {"data": [{"createdDate": r.CreatedDate, "utcCreatedDate": r.UtcCreatedDate, "deviceId": r.DeviceId}]} is all that's really required.
|
NOTE: {"data": [{"createdDate": r.CreatedDate, "utcCreatedDate": r.UtcCreatedDate, "deviceId": r.DeviceId}]} is all that's really required.
|
||||||
*/
|
*/
|
||||||
func (a *Arlo) BatchDeleteRecordings(l Library) error {
|
func (a *Arlo) BatchDeleteRecordings(l *Library) error {
|
||||||
|
|
||||||
body := map[string]Library{"data": l}
|
body := map[string]Library{"data": *l}
|
||||||
resp, err := a.post(LibraryRecycleUri, "", body, nil)
|
fmt.Printf("%+v\n", body)
|
||||||
return checkRequest(resp, err, "failed to delete recordings")
|
return nil
|
||||||
|
//resp, err := a.post(LibraryRecycleUri, "", body, nil)
|
||||||
|
//return checkRequest(resp, err, "failed to delete recordings")
|
||||||
}
|
}
|
||||||
|
20
responses.go
20
responses.go
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
// LoginResponse is an intermediate struct used when parsing data from the Login() call.
|
// LoginResponse is an intermediate struct used when parsing data from the Login() call.
|
||||||
@ -24,12 +40,12 @@ type LibraryResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type StreamResponse struct {
|
type StreamResponse struct {
|
||||||
Data StreamUrl
|
URL string `json:"url"`
|
||||||
Status
|
Status
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecordingResponse struct {
|
type RecordingResponse struct {
|
||||||
Data StreamUrl
|
URL string `json:"url"`
|
||||||
Status
|
Status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
types.go
28
types.go
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -59,7 +75,7 @@ type Connectivity struct {
|
|||||||
ActiveNetwork string `json:"activeNetwork,omitempty"`
|
ActiveNetwork string `json:"activeNetwork,omitempty"`
|
||||||
APN string `json:"apn,omitempty"`
|
APN string `json:"apn,omitempty"`
|
||||||
CarrierFw string `json:"carrierFw,omitempty"`
|
CarrierFw string `json:"carrierFw,omitempty"`
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected,omitempty"`
|
||||||
FWVersion string `json:"fwVersion,omitempty"`
|
FWVersion string `json:"fwVersion,omitempty"`
|
||||||
ICCID string `json:"iccid,omitempty"`
|
ICCID string `json:"iccid,omitempty"`
|
||||||
IMEI string `json:"imei,omitempty"`
|
IMEI string `json:"imei,omitempty"`
|
||||||
@ -115,10 +131,6 @@ type Favorite struct {
|
|||||||
Favorite uint8 `json:"Favorite"`
|
Favorite uint8 `json:"Favorite"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StreamUrl struct {
|
|
||||||
Url string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BaseDetectionProperties struct {
|
type BaseDetectionProperties struct {
|
||||||
Armed bool `json:"armed"`
|
Armed bool `json:"armed"`
|
||||||
Sensitivity int `json:"sensitivity"`
|
Sensitivity int `json:"sensitivity"`
|
||||||
@ -240,8 +252,8 @@ type EventStreamPayload struct {
|
|||||||
To string `json:"to"`
|
To string `json:"to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data is part of the Status message fragment returned by most calls to the Arlo API.
|
// URL is part of the Status message fragment returned by most calls to the Arlo API.
|
||||||
// Data is only populated when Success is false.
|
// URL is only populated when Success is false.
|
||||||
type Data struct {
|
type Data struct {
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Reason string `json:"reason,omitempty"`
|
Reason string `json:"reason,omitempty"`
|
||||||
@ -250,6 +262,6 @@ type Data struct {
|
|||||||
|
|
||||||
// Status is the message fragment returned from most http calls to the Arlo API.
|
// Status is the message fragment returned from most http calls to the Arlo API.
|
||||||
type Status struct {
|
type Status struct {
|
||||||
Data `json:"Data,omitempty"`
|
Data `json:"URL,omitempty"`
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
76
util.go
76
util.go
@ -1,10 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Jeffrey Walter <jeffreydwalter@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
package arlo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -15,25 +33,12 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkHttpRequest(resp *request.Response, err error, msg string) error {
|
func checkRequest(resp *request.Response, err error, msg string) error {
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return errors.WithMessage(errors.New(fmt.Sprintf("http request failed: %s (%d)", resp.Status, resp.StatusCode)), msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, msg)
|
return errors.WithMessage(err, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRequest(resp *request.Response, err error, msg string) error {
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if err := checkHttpRequest(resp, err, msg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var status Status
|
var status Status
|
||||||
if err := resp.Decode(&status); err != nil {
|
if err := resp.Decode(&status); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -59,31 +64,38 @@ func genTransId() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Arlo) get(uri, xCloudId string, header http.Header) (*request.Response, error) {
|
func (a *Arlo) get(uri, xCloudId string, header http.Header) (*request.Response, error) {
|
||||||
if len(xCloudId) > 0 {
|
a.client.AddHeader("xcloudId", xCloudId)
|
||||||
a.rwmutex.Lock()
|
|
||||||
a.client.BaseHttpHeader.Set("xcloudId", xCloudId)
|
|
||||||
a.rwmutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.client.Get(uri, header)
|
return a.client.Get(uri, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Arlo) put(uri, xCloudId string, body interface{}, header http.Header) (*request.Response, error) {
|
func (a *Arlo) put(uri, xCloudId string, body interface{}, header http.Header) (*request.Response, error) {
|
||||||
if len(xCloudId) > 0 {
|
a.client.AddHeader("xcloudId", xCloudId)
|
||||||
a.rwmutex.Lock()
|
|
||||||
a.client.BaseHttpHeader.Set("xcloudId", xCloudId)
|
|
||||||
a.rwmutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.client.Put(uri, body, header)
|
return a.client.Put(uri, body, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Arlo) post(uri, xCloudId string, body interface{}, header http.Header) (*request.Response, error) {
|
func (a *Arlo) post(uri, xCloudId string, body interface{}, header http.Header) (*request.Response, error) {
|
||||||
if len(xCloudId) > 0 {
|
a.client.AddHeader("xcloudId", xCloudId)
|
||||||
a.rwmutex.Lock()
|
|
||||||
a.client.BaseHttpHeader.Set("xcloudId", xCloudId)
|
|
||||||
a.rwmutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.client.Post(uri, body, header)
|
return a.client.Post(uri, body, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Arlo) DownloadFile(url, to string) error {
|
||||||
|
msg := fmt.Sprintf("failed to download file (%s) => (%s)", url, to)
|
||||||
|
resp, err := a.get(url, "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
f, err := os.Create(to)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(f, resp.Body)
|
||||||
|
defer f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user