arlo-go/basestation.go

223 lines
6.4 KiB
Go

package arlo
import (
"context"
"fmt"
"time"
)
const eventStreamTimeout = 30 * time.Second
const pingTime = 30 * time.Second
// A Basestation is a Device that's not type "camera" (basestation, arloq, arloqs, etc.).
// This type is here just for semantics. Some methods explicitly require a device of a certain type.
type Basestation struct {
arlo *Arlo
Device
}
type GetModesResponse struct {
Active string `json:"active"`
Modes []*Mode `json:"modes"`
}
type Mode struct {
Name string `json:"name"`
Type string `json:"type,omitempty"`
RulesIds []string `json:"rules"`
ID string `json:"id"`
}
type GetRulesResponse struct {
Rules []Rule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Protected bool `json:"protected"`
Triggers []struct {
Type string `json:"type"`
DeviceID string `json:"deviceId"`
Sensitivity int `json:"sensitivity"`
} `json:"triggers"`
Actions []struct {
Type string `json:"type"`
Recipients []string `json:"recipients,omitempty"`
DeviceID string `json:"deviceId,omitempty"`
StopCondition struct {
Type string `json:"type"`
DeviceID string `json:"deviceId"`
} `json:"stopCondition,omitempty"`
} `json:"actions"`
ID string `json:"id"`
}
type CalendarMode struct {
Active bool `json:"active"`
Schedule []struct {
ModeID string `json:"modeId"`
StartTime int `json:"startTime"`
} `json:"schedule"`
}
func NewBaseStation(arlo *Arlo, device Device) *Basestation {
return &Basestation{
arlo: arlo,
Device: device,
}
}
// makeEventStreamRequest is a helper function sets up a response channel, sends a message to the event stream, and blocks waiting for the response.
func (b *Basestation) GetState(ctx context.Context) (*BaseStationState, error) {
var state BaseStationState
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "get", "basestation", false, nil, &state)
if err != nil {
return nil, fmt.Errorf("getting basestation %s state: %v", b.DeviceName, err)
}
return &state, nil
}
func (b *Basestation) GetAllCameraState(ctx context.Context) ([]CameraState, error) {
var states []CameraState
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "get", "cameras", false, nil, &states)
if err != nil {
return nil, fmt.Errorf("getting associated cameras state: %v", err)
}
return states, nil
}
func (b *Basestation) GetRules(ctx context.Context) ([]Rule, error) {
var resp GetRulesResponse
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "get", "rules", false, nil, &resp)
if err != nil {
return nil, fmt.Errorf("getting rules: %v", err)
}
return resp.Rules, nil
}
func (b *Basestation) GetCalendarMode(ctx context.Context) (*CalendarMode, error) {
var calendarMode CalendarMode
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "get", "schedule", false, nil, &calendarMode)
if err != nil {
return nil, fmt.Errorf("getting calendar mode: %v", err)
}
return &calendarMode, nil
}
// SetCalendarMode toggles calendar mode.
// NOTE: The Arlo API seems to disable calendar mode when switching to other modes, if it's enabled.
// You should probably do the same, although, the UI reflects the switch from calendar mode to say armed mode without explicitly setting calendar mode to inactive.
func (b *Basestation) SetCalendarMode(ctx context.Context, active bool) error {
resp := make(map[string]bool)
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "set", "schedule", true, struct {
Active bool `json:"active"`
}{
Active: active,
}, &resp)
if err != nil {
return fmt.Errorf("setting calendar mode %t: %v", active, err)
}
activemode, ok := resp["active"]
if !ok {
return fmt.Errorf("active mode not present in response")
}
if activemode != active {
return fmt.Errorf("active mode is not the mode requested: requested %t, set %t", active, activemode)
}
return nil
}
func (b *Basestation) GetModes(ctx context.Context) (*GetModesResponse, error) {
var resp GetModesResponse
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "get", "modes", false, nil, &resp)
if err != nil {
return nil, fmt.Errorf("getting modes: %v", err)
}
return &resp, nil
}
func (b *Basestation) SetCustomMode(ctx context.Context, mode string) error {
resp := make(map[string]string)
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "set", "modes", true, struct {
Active string `json:"active"`
}{
Active: mode,
}, &resp)
if err != nil {
return fmt.Errorf("setting custom mode %s: %v", mode, err)
}
activemode, ok := resp["active"]
if !ok {
return fmt.Errorf("active mode not present in response")
}
if activemode != mode {
return fmt.Errorf("active mode is not the mode requested: requested %s, set %s", mode, activemode)
}
return nil
}
func (b *Basestation) DeleteMode(ctx context.Context, mode string) error {
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "delete", fmt.Sprintf("modes/%s", mode), true, nil, nil)
if err != nil {
return fmt.Errorf("deleting mode %s: %v", mode, err)
}
return nil
}
func (b *Basestation) Arm(ctx context.Context) error {
err := b.SetCustomMode(ctx, "mode1")
if err != nil {
return fmt.Errorf("arming (mode1): %v", err)
}
return nil
}
func (b *Basestation) Disarm(ctx context.Context) error {
err := b.SetCustomMode(ctx, "mode0")
if err != nil {
return fmt.Errorf("disarming (mode0): %v", err)
}
return nil
}
type SetSirenResponse struct {
SirenState string `json:"sirenState"`
SirenTrigger string `json:"sirenTrigger"`
Duration int `json:"duration"`
Timestamp int64 `json:"timestamp"`
}
func (b *Basestation) SirenOn(ctx context.Context) error {
var response SetSirenResponse
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "set", "siren", true, SirenProperties{
SirenState: "on",
Duration: 300,
Volume: 8,
Pattern: "alarm",
}, &response)
if err != nil {
return fmt.Errorf("making request: %v", err)
}
if response.SirenState != "on" {
return fmt.Errorf("siren not on in response")
}
return nil
}
func (b *Basestation) SirenOff(ctx context.Context) error {
var response SetSirenResponse
err := b.arlo.makeRequest(ctx, b.DeviceId, b.XCloudId, "set", "siren", true, SirenProperties{
SirenState: "off",
Duration: 300,
Volume: 8,
Pattern: "alarm",
}, &response)
if err != nil {
return fmt.Errorf("making request: %v", err)
}
if response.SirenState != "off" {
return fmt.Errorf("siren not off in response")
}
return nil
}