Skip to content

Commit 7404811

Browse files
authored
Merge pull request #66 from golift/dn2_level_2
Fix a whole slew of problems
2 parents 38a802d + 2e57737 commit 7404811

21 files changed

+826
-217
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ A command line interface app that uses this library exists. Most of the testing
1515
Find it here: [https://github.com/davidnewhall/SecSpyCLI](https://github.com/davidnewhall/SecSpyCLI)
1616
It's full of great examples on how to use this library, and can be easily installed with homebrew.
1717

18-
- Works with SecuritySpy 4 and 5.
18+
- Works with SecuritySpy 4 and 5 and probably 6.
1919
- There's a lot more to learn about this package in [GODOC](https://godoc.org/golift.io/securityspy).
2020

2121
## FEATURES

cameras.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"image"
77
"image/jpeg"
88
"io"
9+
"net/http"
910
"net/url"
1011
"os"
1112
"strconv"
@@ -116,7 +117,13 @@ func (c *Camera) SaveVideo(ops *VidOps, length time.Duration, maxsize int64, out
116117
// StreamMJPG makes a web request to retrieve a motion JPEG stream.
117118
// Returns an io.ReadCloser that will (hopefully) never end.
118119
func (c *Camera) StreamMJPG(ops *VidOps) (io.ReadCloser, error) {
119-
resp, err := c.server.Get("++video", c.makeRequestParams(ops))
120+
return c.StreamMJPGContext(context.Background(), ops)
121+
}
122+
123+
// StreamMJPGContext makes a web request to retrieve a motion JPEG stream.
124+
// Returns an io.ReadCloser that will (hopefully) never end.
125+
func (c *Camera) StreamMJPGContext(ctx context.Context, ops *VidOps) (io.ReadCloser, error) {
126+
resp, err := c.server.GetContextClient(ctx, "++video", c.makeRequestParams(ops), c.streamHTTPClient())
120127
if err != nil {
121128
return nil, fmt.Errorf("getting video: %w", err)
122129
}
@@ -127,7 +134,13 @@ func (c *Camera) StreamMJPG(ops *VidOps) (io.ReadCloser, error) {
127134
// StreamH264 makes a web request to retrieve an H264 stream.
128135
// Returns an io.ReadCloser that will (hopefully) never end.
129136
func (c *Camera) StreamH264(ops *VidOps) (io.ReadCloser, error) {
130-
resp, err := c.server.Get("++stream", c.makeRequestParams(ops))
137+
return c.StreamH264Context(context.Background(), ops)
138+
}
139+
140+
// StreamH264Context makes a web request to retrieve an H264 stream.
141+
// Returns an io.ReadCloser that will (hopefully) never end.
142+
func (c *Camera) StreamH264Context(ctx context.Context, ops *VidOps) (io.ReadCloser, error) {
143+
resp, err := c.server.GetContextClient(ctx, "++stream", c.makeRequestParams(ops), c.streamHTTPClient())
131144
if err != nil {
132145
return nil, fmt.Errorf("getting stream: %w", err)
133146
}
@@ -138,7 +151,13 @@ func (c *Camera) StreamH264(ops *VidOps) (io.ReadCloser, error) {
138151
// StreamG711 makes a web request to retrieve an G711 audio stream.
139152
// Returns an io.ReadCloser that will (hopefully) never end.
140153
func (c *Camera) StreamG711() (io.ReadCloser, error) {
141-
resp, err := c.server.Get("++audio", c.makeRequestParams(nil))
154+
return c.StreamG711Context(context.Background())
155+
}
156+
157+
// StreamG711Context makes a web request to retrieve an G711 audio stream.
158+
// Returns an io.ReadCloser that will (hopefully) never end.
159+
func (c *Camera) StreamG711Context(ctx context.Context) (io.ReadCloser, error) {
160+
resp, err := c.server.GetContextClient(ctx, "++audio", c.makeRequestParams(nil), c.streamHTTPClient())
142161
if err != nil {
143162
return nil, fmt.Errorf("getting audio: %w", err)
144163
}
@@ -165,6 +184,10 @@ func (c *Camera) PostG711(audio io.ReadCloser) ([]byte, error) {
165184
// GetJPEG returns an images from a camera.
166185
// VidOps defines the image size. ops.FPS is ignored.
167186
func (c *Camera) GetJPEG(ops *VidOps) (image.Image, error) {
187+
if ops == nil {
188+
ops = &VidOps{}
189+
}
190+
168191
ops.FPS = -1 // not used for single image
169192

170193
ctx, cancel := context.WithTimeout(context.Background(), c.server.TimeoutDur())
@@ -329,3 +352,10 @@ func (c *Camera) makeRequestParams(ops *VidOps) url.Values {
329352

330353
return params
331354
}
355+
356+
func (c *Camera) streamHTTPClient() *http.Client {
357+
client := c.server.HTTPClient()
358+
client.Timeout = 0
359+
360+
return client
361+
}

cameras_commands_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package securityspy_test
2+
3+
import (
4+
"encoding/xml"
5+
"net/url"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
"go.uber.org/mock/gomock"
10+
"golift.io/securityspy"
11+
"golift.io/securityspy/mocks"
12+
"golift.io/securityspy/server"
13+
)
14+
15+
func testServerWithCamera(t *testing.T) (*securityspy.Server, *mocks.MockAPI, *securityspy.Camera) {
16+
t.Helper()
17+
18+
mockCtrl := gomock.NewController(t)
19+
t.Cleanup(mockCtrl.Finish)
20+
21+
secspyServer := securityspy.NewMust(
22+
&server.Config{Username: "user", Password: "pass", URL: "http://127.0.0.1:5678", VerifySSL: false})
23+
fake := mocks.NewMockAPI(mockCtrl)
24+
secspyServer.API = fake
25+
26+
fake.EXPECT().GetXML(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Do(
27+
func(_, _, v any) {
28+
_ = xml.Unmarshal([]byte(testSystemInfo), &v)
29+
},
30+
)
31+
require.NoError(t, secspyServer.Refresh())
32+
33+
camera := secspyServer.Cameras.ByNum(1)
34+
require.NotNil(t, camera)
35+
36+
return secspyServer, fake, camera
37+
}
38+
39+
func TestToggleContinuousUsesNumericArmValues(t *testing.T) {
40+
t.Parallel()
41+
42+
_, fake, camera := testServerWithCamera(t)
43+
44+
fake.EXPECT().SimpleReq("++ssControlContinuous", url.Values{"arm": []string{"0"}}, camera.Number)
45+
require.NoError(t, camera.ToggleContinuous(securityspy.CameraDisarm))
46+
47+
fake.EXPECT().SimpleReq("++ssControlContinuous", url.Values{"arm": []string{"1"}}, camera.Number)
48+
require.NoError(t, camera.ToggleContinuous(securityspy.CameraArm))
49+
}
50+
51+
func TestToggleMotionUsesNumericArmValues(t *testing.T) {
52+
t.Parallel()
53+
54+
_, fake, camera := testServerWithCamera(t)
55+
56+
fake.EXPECT().SimpleReq("++ssControlMotionCapture", url.Values{"arm": []string{"1"}}, camera.Number)
57+
require.NoError(t, camera.ToggleMotion(securityspy.CameraArm))
58+
}
59+
60+
func TestToggleActionsUsesNumericArmValues(t *testing.T) {
61+
t.Parallel()
62+
63+
_, fake, camera := testServerWithCamera(t)
64+
65+
fake.EXPECT().SimpleReq("++ssControlActions", url.Values{"arm": []string{"0"}}, camera.Number)
66+
require.NoError(t, camera.ToggleActions(securityspy.CameraDisarm))
67+
}

cameras_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import (
44
"encoding/xml"
55
"testing"
66

7-
"github.com/golang/mock/gomock"
87
"github.com/stretchr/testify/assert"
98
"github.com/stretchr/testify/require"
9+
"go.uber.org/mock/gomock"
1010
"golift.io/securityspy"
1111
"golift.io/securityspy/mocks"
1212
"golift.io/securityspy/server"

cameras_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ type CameraArmMode rune
1414
// Arming is either 0 or 1.
1515
// Use these constants as inputs to a camera's schedule methods.
1616
const (
17-
CameraDisarm CameraArmMode = iota
18-
CameraArm
17+
CameraDisarm CameraArmMode = '0'
18+
CameraArm CameraArmMode = '1'
1919
)
2020

2121
// VidOps are the frame options for a video that can be requested from SecuritySpy.

0 commit comments

Comments
 (0)