EventSteam working, still needs cleanup.
This commit is contained in:
parent
ceb2bff6c8
commit
011447ef60
57
Gopkg.lock
generated
57
Gopkg.lock
generated
@ -2,20 +2,71 @@
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:63eb8e7863bcc41b162d76c3e1b5dd4991fd01f29585017b3a8bab7a5928edd3"
|
||||
name = "github.com/r3labs/sse"
|
||||
packages = ["."]
|
||||
revision = "ab73c814bbdece537f16e92302cd99d1618d0e0d"
|
||||
version = "1.0.1"
|
||||
pruneopts = ""
|
||||
revision = "ad82e5b42970ce737f0ce61490a1607331299496"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:fbdbb6cf8db3278412c9425ad78b26bb8eb788181f26a3ffb3e4f216b314f86a"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"idna",
|
||||
"publicsuffix",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1cf61f0228e64d1bfddaa42bfd1f5047ed3626925b9bea69bbcb9168472dae23"
|
||||
name = "gopkg.in/cenkalti/backoff.v1"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "61153c768f31ee5f130071d08fc82b85208528de"
|
||||
version = "v1.1.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "559855ebed7f1c0bf0bea2b6f750822d2eb67595d23a2f9006a8302a22b74e47"
|
||||
input-imports = [
|
||||
"github.com/pkg/errors",
|
||||
"github.com/r3labs/sse",
|
||||
"golang.org/x/net/publicsuffix",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@ -28,3 +28,7 @@
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/r3labs/sse"
|
||||
|
17
account.go
17
account.go
@ -2,7 +2,6 @@ package arlo_golang
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
@ -58,7 +57,6 @@ func Login(user string, pass string) (*Arlo, error) {
|
||||
|
||||
body := map[string]string{"email": a.user, "password": a.pass}
|
||||
resp, err := a.client.Post(LoginUri, body, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "login request failed")
|
||||
}
|
||||
@ -72,6 +70,12 @@ func Login(user string, pass string) (*Arlo, error) {
|
||||
// Cache the auth token.
|
||||
a.client.BaseHttpHeader.Add("Authorization", loginResponse.Data.Token)
|
||||
|
||||
// Add other important headers.
|
||||
a.client.BaseHttpHeader.Add("DNT", "1")
|
||||
a.client.BaseHttpHeader.Add("schemaVersion", "1")
|
||||
a.client.BaseHttpHeader.Add("Host", "arlo.netgear.com")
|
||||
a.client.BaseHttpHeader.Add("Referer", "https://arlo.netgear.com/")
|
||||
|
||||
// Save the account info with the Arlo struct.
|
||||
a.Account = loginResponse.Data
|
||||
|
||||
@ -83,17 +87,16 @@ func Login(user string, pass string) (*Arlo, error) {
|
||||
}
|
||||
|
||||
// Set the XCloudId header for future requests. You can override this on a per-request basis if needed.
|
||||
a.client.BaseHttpHeader.Add("xCloudId", deviceResponse.Data[0].XCloudId)
|
||||
a.client.BaseHttpHeader.Add("xcloudId", deviceResponse.Data[0].XCloudId)
|
||||
|
||||
// Cache the devices as their respective types.
|
||||
a.Cameras = deviceResponse.Data.GetCameras()
|
||||
a.Basestations = deviceResponse.Data.GetBasestations()
|
||||
// Connect each basestation to the EventStream.
|
||||
for i := range a.Basestations {
|
||||
a.Basestations[i].connect(a)
|
||||
a.Basestations[i].arlo = a
|
||||
a.Basestations[i].Subscribe()
|
||||
}
|
||||
|
||||
log.Printf("HERE: %v", util.PrettyPrint(a.Basestations))
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("failed to login")
|
||||
@ -105,7 +108,6 @@ func Login(user string, pass string) (*Arlo, error) {
|
||||
func (a *Arlo) Logout() (*Status, error) {
|
||||
|
||||
resp, err := a.client.Put(LogoutUri, nil, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "logout request failed")
|
||||
}
|
||||
@ -123,7 +125,6 @@ func (a *Arlo) UpdateProfile(firstName, lastName string) (*Status, error) {
|
||||
|
||||
body := map[string]string{"firstName": firstName, "lastName": lastName}
|
||||
resp, err := a.client.Put(UserProfileUri, body, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to update profile")
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package arlo_golang
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jeffreydwalter/arlo-golang/internal/util"
|
||||
"github.com/pkg/errors"
|
||||
@ -38,14 +36,39 @@ type BaseStationMetadata struct {
|
||||
type Basestation struct {
|
||||
Device
|
||||
eventStream *EventStream
|
||||
arlo *Arlo
|
||||
}
|
||||
|
||||
// Basestations is an array of Basestation objects.
|
||||
type Basestations []Basestation
|
||||
|
||||
func (b *Basestation) connect(a *Arlo) {
|
||||
b.eventStream = NewEventStream(BaseUrl+fmt.Sprintf(SubscribeUri, a.Account.Token), util.HeaderToMap(*a.client.BaseHttpHeader))
|
||||
func (b *Basestation) Subscribe() (*Status, error) {
|
||||
b.eventStream = NewEventStream(BaseUrl+fmt.Sprintf(SubscribeUri, b.arlo.Account.Token), b.arlo.client.HttpClient, util.HeaderToMap(*b.arlo.client.BaseHttpHeader))
|
||||
b.eventStream.Listen()
|
||||
|
||||
transId := GenTransId()
|
||||
|
||||
body := NotifyPayload{
|
||||
Action: "set",
|
||||
Resource: fmt.Sprintf("subscriptions/%s_%s", b.UserId, "web"),
|
||||
PublishResponse: false,
|
||||
Properties: map[string][]string{"devices": []string{b.DeviceId}},
|
||||
TransId: transId,
|
||||
From: fmt.Sprintf("%s_%s", b.UserId, TransIdPrefix),
|
||||
To: b.DeviceId,
|
||||
}
|
||||
|
||||
resp, err := b.arlo.client.Post(fmt.Sprintf(NotifyUri, b.DeviceId), body, nil)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to subscribe to the event stream")
|
||||
}
|
||||
|
||||
var status Status
|
||||
if err := resp.Decode(&status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
/*
|
||||
@ -64,7 +87,7 @@ func (b *Basestation) connect(a *Arlo) {
|
||||
"id":"XXX-XXXXXXX"
|
||||
}
|
||||
*/
|
||||
func (a *Arlo) GetBasestationState(b Basestation) (*NotifyResponse, error) {
|
||||
func (b *Basestation) GetState() (*NotifyResponse, error) {
|
||||
|
||||
transId := GenTransId()
|
||||
|
||||
@ -72,37 +95,31 @@ func (a *Arlo) GetBasestationState(b Basestation) (*NotifyResponse, error) {
|
||||
Action: "get",
|
||||
Resource: "basestation",
|
||||
PublishResponse: false,
|
||||
Properties: map[string]string{},
|
||||
TransId: transId,
|
||||
From: fmt.Sprintf("%s_%s", b.UserId, TransIdPrefix),
|
||||
To: b.DeviceId,
|
||||
}
|
||||
|
||||
//fmt.Printf("BODY: %+v\n", body)
|
||||
//fmt.Printf("HEADERS: %+v\n", a.client.BaseHttpHeader)
|
||||
|
||||
fmt.Println("Subscribing to the eventstream.")
|
||||
b.eventStream.Subscriptions[transId] = new(Subscriber)
|
||||
|
||||
for b.eventStream.Connected == false {
|
||||
fmt.Println("Not connected yet.")
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
fmt.Println("Connected now.")
|
||||
|
||||
resp, err := a.client.Post(fmt.Sprintf(NotifyUri, b.DeviceId), body, nil)
|
||||
resp, err := b.arlo.client.Post(fmt.Sprintf(NotifyUri, b.DeviceId), body, nil)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to start stream")
|
||||
return nil, errors.WithMessage(err, "failed to get basestation state")
|
||||
}
|
||||
|
||||
ep := &NotifyResponse{}
|
||||
err = json.NewDecoder(resp.Body).Decode(ep)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to decode body")
|
||||
var status Status
|
||||
if err := resp.Decode(&status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
fmt.Println("Subscribing to the eventstream.")
|
||||
select {
|
||||
case notifyResponse := <-*b.eventStream.Subscriptions[transId]:
|
||||
fmt.Println("Recieved a response from the subscription.")
|
||||
if !status.Success {
|
||||
return nil, errors.New("failed to get basestation status")
|
||||
}
|
||||
|
||||
notifyResponse := <-*b.eventStream.Subscriptions[transId]
|
||||
return ¬ifyResponse, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
devices.go
12
devices.go
@ -111,10 +111,10 @@ func (ds *Devices) GetCameras() Cameras {
|
||||
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")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var deviceResponse DeviceResponse
|
||||
if err := resp.Decode(&deviceResponse); err != nil {
|
||||
@ -132,11 +132,12 @@ func (a *Arlo) GetDevices() (*DeviceResponse, error) {
|
||||
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)
|
||||
|
||||
resp, err := a.client.Put(DeviceRenameUri, body, nil)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to update device name")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status Status
|
||||
if err := resp.Decode(&status); err != nil {
|
||||
@ -155,6 +156,7 @@ func (a *Arlo) UpdateDisplayOrder(d DeviceOrder) (*Status, error) {
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to update display order")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status Status
|
||||
if err := resp.Decode(&status); err != nil {
|
||||
@ -185,10 +187,10 @@ func (a *Arlo) StartStream(c Camera) (*StreamResponse, error) {
|
||||
}
|
||||
|
||||
resp, err := a.client.Post(DeviceStartStreamUri, body, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to start stream")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var streamResponse StreamResponse
|
||||
if err := resp.Decode(&streamResponse); err != nil {
|
||||
@ -209,10 +211,12 @@ func (a *Arlo) TakeSnapshot(c Camera) (*StreamResponse, error) {
|
||||
}
|
||||
|
||||
body := map[string]string{"deviceId": c.DeviceId, "parentId": c.ParentId, "xcloudId": c.XCloudId, "olsonTimeZone": c.Properties.OlsonTimeZone}
|
||||
|
||||
resp, err := a.client.Post(DeviceTakeSnapshotUri, body, nil)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to take snapshot")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status Status
|
||||
if err := resp.Decode(&status); err != nil {
|
||||
@ -233,10 +237,12 @@ func (a *Arlo) StartRecording(c Camera) (*StreamResponse, error) {
|
||||
}
|
||||
|
||||
body := map[string]string{"deviceId": c.DeviceId, "parentId": c.ParentId, "xcloudId": c.XCloudId, "olsonTimeZone": c.Properties.OlsonTimeZone}
|
||||
|
||||
resp, err := a.client.Post(DeviceStartRecordUri, body, nil)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to start recording")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status Status
|
||||
if err := resp.Decode(&status); err != nil {
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -12,11 +12,11 @@ import (
|
||||
"github.com/r3labs/sse"
|
||||
)
|
||||
|
||||
var FAILED_TO_PUBLISH = errors.New("Failed to publish")
|
||||
|
||||
var FAILED_TO_DECODE_JSON = errors.New("Failed to decode JSON")
|
||||
|
||||
var FAILED_TO_SUBSCRIBE = errors.New("Failed to subscribe to SSEClient")
|
||||
var (
|
||||
FAILED_TO_PUBLISH = errors.New("Failed to publish")
|
||||
FAILED_TO_DECODE_JSON = errors.New("Failed to decode JSON")
|
||||
FAILED_TO_SUBSCRIBE = errors.New("Failed to subscribe to SSEClient")
|
||||
)
|
||||
|
||||
type Subscriber chan NotifyResponse
|
||||
|
||||
@ -33,9 +33,10 @@ type EventStream struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewEventStream(url string, headers map[string]string) *EventStream {
|
||||
func NewEventStream(url string, client *http.Client, headers map[string]string) *EventStream {
|
||||
|
||||
SSEClient := sse.NewClient(url)
|
||||
SSEClient.Connection = client
|
||||
SSEClient.Headers = headers
|
||||
|
||||
return &EventStream{
|
||||
@ -49,35 +50,13 @@ func NewEventStream(url string, headers map[string]string) *EventStream {
|
||||
func (e *EventStream) Listen() {
|
||||
|
||||
go func() {
|
||||
err := e.SSEClient.SubscribeChan("", e.Events)
|
||||
err := e.SSEClient.SubscribeChanRaw(e.Events)
|
||||
if err != nil {
|
||||
fmt.Println(FAILED_TO_SUBSCRIBE)
|
||||
e.ErrorChan <- FAILED_TO_SUBSCRIBE
|
||||
}
|
||||
}()
|
||||
|
||||
for event := range e.Events {
|
||||
fmt.Println("Got event message here.")
|
||||
fmt.Printf("EVENT: %s\n", event.Event)
|
||||
fmt.Printf("DATA: %s\n", event.Data)
|
||||
|
||||
if event.Data != nil {
|
||||
notifyResponse := &NotifyResponse{}
|
||||
b := bytes.NewBuffer(event.Data)
|
||||
err := json.NewDecoder(b).Decode(notifyResponse)
|
||||
if err != nil {
|
||||
e.ErrorChan <- errors.WithMessage(err, "failed to decode JSON")
|
||||
break
|
||||
}
|
||||
|
||||
if notifyResponse.Status == "connected" {
|
||||
e.Connected = true
|
||||
fmt.Println("Connected.")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
for event := range e.Events {
|
||||
fmt.Println("Got event message.")
|
||||
@ -93,6 +72,7 @@ func (e *EventStream) Listen() {
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", notifyResponse)
|
||||
if notifyResponse.Status == "connected" {
|
||||
fmt.Println("Connected.")
|
||||
e.Connected = true
|
||||
@ -100,7 +80,7 @@ func (e *EventStream) Listen() {
|
||||
fmt.Println("Disconnected.")
|
||||
e.Connected = false
|
||||
} else {
|
||||
fmt.Printf("Message for transId: %s", notifyResponse.TransId)
|
||||
fmt.Printf("Message for transId: %s\n", notifyResponse.TransId)
|
||||
if subscriber, ok := e.Subscriptions[notifyResponse.TransId]; ok {
|
||||
e.Lock()
|
||||
*subscriber <- *notifyResponse
|
||||
@ -112,36 +92,9 @@ func (e *EventStream) Listen() {
|
||||
fmt.Println("Throwing away message.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
/*
|
||||
go func() {
|
||||
|
||||
fmt.Println("go func to recieve a subscription.")
|
||||
for {
|
||||
fmt.Println("go func for loop to recieve a subscription.")
|
||||
select {
|
||||
case s := <-e.Subscriptions:
|
||||
if resp, ok := e.Responses[s.transId]; ok {
|
||||
fmt.Println("Recieved a subscription, sending response.")
|
||||
s.ResponseChan <- resp
|
||||
e.Lock()
|
||||
delete(e.Responses, s.transId)
|
||||
e.Unlock()
|
||||
} else {
|
||||
fmt.Println("Recieved a subscription error, sending error response.")
|
||||
e.ErrorChan <- FAILED_TO_PUBLISH
|
||||
break
|
||||
}
|
||||
fmt.Printf("Event data was nil.\n")
|
||||
}
|
||||
}
|
||||
}()
|
||||
*/
|
||||
}
|
||||
|
||||
func (e *EventStream) verbose(params ...interface{}) {
|
||||
if e.Verbose {
|
||||
log.Println(params...)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@ -21,7 +23,7 @@ func NewClient(baseurl string) (*Client, error) {
|
||||
var err error
|
||||
var jar *cookiejar.Jar
|
||||
|
||||
options := cookiejar.Options{}
|
||||
options := cookiejar.Options{PublicSuffixList: publicsuffix.List}
|
||||
|
||||
if jar, err = cookiejar.New(&options); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create client object")
|
||||
@ -33,7 +35,7 @@ func NewClient(baseurl string) (*Client, error) {
|
||||
}
|
||||
|
||||
header := make(http.Header)
|
||||
header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36")
|
||||
header.Add("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 NETGEAR/v1 (iOS Vuezone)")
|
||||
header.Add("Content-Type", "application/json")
|
||||
header.Add("Accept", "application/json")
|
||||
|
||||
@ -78,7 +80,7 @@ func (c *Client) newRequest(method string, uri string, body interface{}, header
|
||||
return nil, errors.Wrap(err, "failed to create request object")
|
||||
}
|
||||
}
|
||||
// log.Printf("JSON: %v", buf)
|
||||
|
||||
u := c.BaseURL.String() + uri
|
||||
req, err := http.NewRequest(method, u, buf)
|
||||
if err != nil {
|
||||
|
@ -19,7 +19,6 @@ type Response struct {
|
||||
func (resp *Response) GetContentType() (string, error) {
|
||||
|
||||
mediaType, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to get content type")
|
||||
}
|
||||
|
2
types.go
2
types.go
@ -53,7 +53,7 @@ type StreamUrl struct {
|
||||
type NotifyPayload struct {
|
||||
Action string `json:"action,omitempty"`
|
||||
Resource string `json:"resource,omitempty"`
|
||||
PublishResponse bool `json:"publishResponse,omitempty"`
|
||||
PublishResponse bool `json:"publishResponse"`
|
||||
Properties interface{} `json:"properties,omitempty"`
|
||||
TransId string `json:"transId"`
|
||||
From string `json:"from"`
|
||||
|
Loading…
Reference in New Issue
Block a user