-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import old device discovery capability and update.
- Loading branch information
Showing
6 changed files
with
353 additions
and
7 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package zda | ||
|
||
import "context" | ||
|
||
type eventSender interface { | ||
sendEvent(event interface{}) | ||
} | ||
|
||
func (g *gateway) sendEvent(event interface{}) { | ||
//TODO implement me | ||
panic("implement me") | ||
} | ||
|
||
func (g *gateway) ReadEvent(_ context.Context) (interface{}, error) { | ||
//TODO implement me | ||
panic("implement me") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package zda | ||
|
||
import "github.com/stretchr/testify/mock" | ||
|
||
type mockEventSender struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *mockEventSender) sendEvent(event interface{}) { | ||
m.Called(event) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package zda | ||
|
||
import ( | ||
"context" | ||
"github.com/shimmeringbee/da" | ||
"github.com/shimmeringbee/da/capabilities" | ||
"github.com/shimmeringbee/logwrap" | ||
"github.com/shimmeringbee/zigbee" | ||
"time" | ||
) | ||
|
||
type deviceDiscovery struct { | ||
gateway da.Gateway | ||
networkJoining zigbee.NetworkJoining | ||
eventSender eventSender | ||
|
||
discovering bool | ||
allowTimer *time.Timer | ||
allowExpiresAt time.Time | ||
|
||
logger logwrap.Logger | ||
} | ||
|
||
func (d *deviceDiscovery) Capability() da.Capability { | ||
return capabilities.DeviceDiscoveryFlag | ||
} | ||
|
||
func (d *deviceDiscovery) Name() string { | ||
return capabilities.StandardNames[d.Capability()] | ||
} | ||
|
||
func (d *deviceDiscovery) Enable(ctx context.Context, duration time.Duration) error { | ||
d.logger.LogInfo(ctx, "Invoking PermitJoin on Zigbee provider.", logwrap.Datum("Duration", duration)) | ||
if err := d.networkJoining.PermitJoin(ctx, true); err != nil { | ||
d.logger.LogError(ctx, "Failed to PermitJoin on Zigbee provider.", logwrap.Err(err)) | ||
return err | ||
} | ||
|
||
if d.allowTimer != nil { | ||
d.allowTimer.Stop() | ||
} | ||
|
||
d.allowExpiresAt = time.Now().Add(duration) | ||
d.allowTimer = time.AfterFunc(duration, func() { | ||
cctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
defer cancel() | ||
|
||
if err := d.Disable(cctx); err != nil { | ||
d.logger.LogError(cctx, "Automatic timed DenyJoin failed.", logwrap.Err(err)) | ||
} | ||
}) | ||
|
||
d.discovering = true | ||
|
||
d.eventSender.sendEvent(capabilities.DeviceDiscoveryEnabled{ | ||
Gateway: d.gateway, | ||
Duration: duration, | ||
}) | ||
return nil | ||
} | ||
|
||
func (d *deviceDiscovery) Disable(ctx context.Context) error { | ||
d.logger.LogInfo(ctx, "Invoking DenyJoin on Zigbee provider.") | ||
if err := d.networkJoining.DenyJoin(ctx); err != nil { | ||
d.logger.LogError(ctx, "Failed to DenyJoin on Zigbee provider.", logwrap.Err(err)) | ||
return err | ||
} | ||
|
||
d.discovering = false | ||
d.allowTimer = nil | ||
d.allowExpiresAt = time.Time{} | ||
|
||
d.eventSender.sendEvent(capabilities.DeviceDiscoveryDisabled{ | ||
Gateway: d.gateway, | ||
}) | ||
return nil | ||
} | ||
|
||
func (d *deviceDiscovery) Status(ctx context.Context) (capabilities.DeviceDiscoveryStatus, error) { | ||
remainingDuration := d.allowExpiresAt.Sub(time.Now()) | ||
if remainingDuration < 0 { | ||
remainingDuration = 0 | ||
} | ||
|
||
return capabilities.DeviceDiscoveryStatus{Discovering: d.discovering, RemainingDuration: remainingDuration}, nil | ||
} | ||
|
||
func (d *deviceDiscovery) Stop() { | ||
if d.allowTimer != nil { | ||
d.allowTimer.Stop() | ||
} | ||
} | ||
|
||
var _ capabilities.DeviceDiscovery = (*deviceDiscovery)(nil) | ||
var _ da.BasicCapability = (*deviceDiscovery)(nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
package zda | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"github.com/shimmeringbee/da/capabilities" | ||
"github.com/shimmeringbee/logwrap" | ||
"github.com/shimmeringbee/logwrap/impl/discard" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
"testing" | ||
"time" | ||
) | ||
|
||
type mockNetworkJoining struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *mockNetworkJoining) PermitJoin(ctx context.Context, allRouters bool) error { | ||
args := m.Called(ctx, allRouters) | ||
return args.Error(0) | ||
} | ||
|
||
func (m *mockNetworkJoining) DenyJoin(ctx context.Context) error { | ||
args := m.Called(ctx) | ||
return args.Error(0) | ||
} | ||
|
||
func TestZigbeeDeviceDiscovery_Enable(t *testing.T) { | ||
t.Run("calling enable on device which is self causes AllowJoin of zigbee provider", func(t *testing.T) { | ||
mockEventSender := mockEventSender{} | ||
mockEventSender.On("sendEvent", mock.IsType(capabilities.DeviceDiscoveryEnabled{})) | ||
|
||
mockNetworkJoining := mockNetworkJoining{} | ||
mockNetworkJoining.On("PermitJoin", mock.Anything, true).Return(nil) | ||
|
||
zdd := deviceDiscovery{ | ||
eventSender: &mockEventSender, | ||
networkJoining: &mockNetworkJoining, | ||
logger: logwrap.New(discard.Discard()), | ||
} | ||
defer zdd.Stop() | ||
|
||
err := zdd.Enable(context.Background(), 500*time.Millisecond) | ||
assert.NoError(t, err) | ||
|
||
status, err := zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.True(t, status.Discovering) | ||
|
||
mockEventSender.AssertExpectations(t) | ||
mockNetworkJoining.AssertExpectations(t) | ||
}) | ||
|
||
t.Run("calling enable on device which is self causes AllowJoin of zigbee provider, and forwards an error", func(t *testing.T) { | ||
mockEventSender := mockEventSender{} | ||
|
||
expectedError := errors.New("error") | ||
mockNetworkJoining := mockNetworkJoining{} | ||
mockNetworkJoining.On("PermitJoin", mock.Anything, true).Return(expectedError) | ||
|
||
zdd := deviceDiscovery{ | ||
eventSender: &mockEventSender, | ||
networkJoining: &mockNetworkJoining, | ||
logger: logwrap.New(discard.Discard()), | ||
} | ||
defer zdd.Stop() | ||
|
||
err := zdd.Enable(context.Background(), 500*time.Millisecond) | ||
assert.Error(t, err) | ||
assert.Equal(t, expectedError, err) | ||
|
||
status, err := zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.False(t, status.Discovering) | ||
|
||
mockEventSender.AssertExpectations(t) | ||
mockNetworkJoining.AssertExpectations(t) | ||
}) | ||
} | ||
|
||
func TestZigbeeDeviceDiscovery_Disable(t *testing.T) { | ||
t.Run("calling disable on device which is self causes DenyJoin of zigbee provider", func(t *testing.T) { | ||
mockEventSender := mockEventSender{} | ||
mockEventSender.On("sendEvent", mock.IsType(capabilities.DeviceDiscoveryDisabled{})) | ||
|
||
mockNetworkJoining := mockNetworkJoining{} | ||
mockNetworkJoining.On("DenyJoin", mock.Anything).Return(nil) | ||
|
||
zdd := deviceDiscovery{ | ||
eventSender: &mockEventSender, | ||
networkJoining: &mockNetworkJoining, | ||
logger: logwrap.New(discard.Discard()), | ||
} | ||
defer zdd.Stop() | ||
|
||
zdd.discovering = true | ||
|
||
err := zdd.Disable(context.Background()) | ||
assert.NoError(t, err) | ||
|
||
status, err := zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.False(t, status.Discovering) | ||
|
||
mockEventSender.AssertExpectations(t) | ||
mockNetworkJoining.AssertExpectations(t) | ||
}) | ||
|
||
t.Run("calling disable on device which is self causes DenyJoin of zigbee provider, and forwards an error", func(t *testing.T) { | ||
expectedError := errors.New("deny join failure") | ||
|
||
mockEventSender := mockEventSender{} | ||
|
||
mockNetworkJoining := mockNetworkJoining{} | ||
mockNetworkJoining.On("DenyJoin", mock.Anything).Return(expectedError) | ||
|
||
zdd := deviceDiscovery{ | ||
eventSender: &mockEventSender, | ||
networkJoining: &mockNetworkJoining, | ||
logger: logwrap.New(discard.Discard()), | ||
} | ||
defer zdd.Stop() | ||
|
||
zdd.discovering = true | ||
|
||
err := zdd.Disable(context.Background()) | ||
|
||
assert.Error(t, err) | ||
assert.Equal(t, expectedError, err) | ||
|
||
status, err := zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.True(t, status.Discovering) | ||
|
||
mockEventSender.AssertExpectations(t) | ||
mockNetworkJoining.AssertExpectations(t) | ||
}) | ||
} | ||
|
||
func TestZigbeeDeviceDiscovery_DurationBehaviour(t *testing.T) { | ||
t.Run("when an allows duration expires then a disable instruction is sent", func(t *testing.T) { | ||
mockEventSender := mockEventSender{} | ||
mockEventSender.On("sendEvent", mock.Anything).Return(nil).Twice() | ||
|
||
mockNetworkJoining := mockNetworkJoining{} | ||
mockNetworkJoining.On("PermitJoin", mock.Anything, true).Return(nil) | ||
mockNetworkJoining.On("DenyJoin", mock.Anything).Return(nil) | ||
|
||
zdd := deviceDiscovery{ | ||
eventSender: &mockEventSender, | ||
networkJoining: &mockNetworkJoining, | ||
logger: logwrap.New(discard.Discard()), | ||
} | ||
|
||
defer zdd.Stop() | ||
|
||
err := zdd.Enable(context.Background(), 100*time.Millisecond) | ||
assert.NoError(t, err) | ||
|
||
status, err := zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.True(t, status.Discovering) | ||
|
||
time.Sleep(150 * time.Millisecond) | ||
|
||
status, err = zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.False(t, status.Discovering) | ||
|
||
mockEventSender.AssertExpectations(t) | ||
mockNetworkJoining.AssertExpectations(t) | ||
}) | ||
|
||
t.Run("second allows extend the duration of the first", func(t *testing.T) { | ||
mockEventSender := mockEventSender{} | ||
mockEventSender.On("sendEvent", mock.Anything).Return(nil).Twice() | ||
|
||
mockNetworkJoining := mockNetworkJoining{} | ||
mockNetworkJoining.On("PermitJoin", mock.Anything, true).Return(nil) | ||
mockNetworkJoining.On("DenyJoin", mock.Anything).Return(nil).Maybe() | ||
|
||
zdd := deviceDiscovery{ | ||
eventSender: &mockEventSender, | ||
networkJoining: &mockNetworkJoining, | ||
logger: logwrap.New(discard.Discard()), | ||
} | ||
|
||
defer zdd.Stop() | ||
|
||
err := zdd.Enable(context.Background(), 50*time.Millisecond) | ||
assert.NoError(t, err) | ||
|
||
status, err := zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.True(t, status.Discovering) | ||
assert.Greater(t, int64(status.RemainingDuration), int64(45*time.Millisecond)) | ||
|
||
err = zdd.Enable(context.Background(), 200*time.Millisecond) | ||
assert.NoError(t, err) | ||
|
||
status, err = zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.True(t, status.Discovering) | ||
assert.Greater(t, int64(status.RemainingDuration), int64(145*time.Millisecond)) | ||
|
||
time.Sleep(150 * time.Millisecond) | ||
|
||
status, err = zdd.Status(context.Background()) | ||
assert.NoError(t, err) | ||
assert.True(t, status.Discovering) | ||
|
||
mockEventSender.AssertExpectations(t) | ||
mockNetworkJoining.AssertExpectations(t) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters