Skip to content
This repository has been archived by the owner on Nov 24, 2023. It is now read-only.

Commit

Permalink
#21 Implemented shutter with Service.GarageDoorOpener. This is more l…
Browse files Browse the repository at this point in the history
…ike a real shutter than Service.LockMechanism.
  • Loading branch information
michbeck100 committed Apr 15, 2016
1 parent 05b1ec5 commit 95740ca
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 31 deletions.
69 changes: 39 additions & 30 deletions accessories/shutter.coffee
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = (env) ->
assert = require 'assert'

module.exports = (env) ->
hap = require 'hap-nodejs'
Service = hap.Service
Characteristic = hap.Characteristic
Expand All @@ -9,44 +10,52 @@ module.exports = (env) ->
##
# ShutterController
#
# currently shutter is using Service.LockMechanism because Service.Window uses percentages
# currently shutter is using Service.GarageDoorOpener because Service.Window uses percentages
# for moving the shutter which is not supported by ShutterController devices
class ShutterAccessory extends BaseAccessory
_targetState: null

constructor: (device) ->
super(device)

@addService(Service.LockMechanism, device.name)
.getCharacteristic(Characteristic.LockTargetState)
@addService(Service.GarageDoorOpener, device.name)
.getCharacteristic(Characteristic.CurrentDoorState)
.on 'get', (callback) =>
@handleReturnPromise(device.getPosition(), callback, @getCurrentState)

device.on 'position', (position) =>
@getService(Service.GarageDoorOpener)
.setCharacteristic(Characteristic.CurrentDoorState, @getCurrentState(position))

@getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.TargetDoorState)
.on 'get', (callback) =>
callback(null, @_targetState)

@getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.TargetDoorState)
.on 'set', (value, callback) =>
promise = null
if value == Characteristic.LockTargetState.UNSECURED
if value == Characteristic.TargetDoorState.OPEN
promise = device.moveUp()
else if value == Characteristic.LockTargetState.SECURED
else if value == Characteristic.TargetDoorState.CLOSED
promise = device.moveDown()
if @_targetState is value
promise = device.stop()
@_targetState = value
if (promise != null)
@handleVoidPromise(promise, callback)

@getService(Service.LockMechanism)
.getCharacteristic(Characteristic.LockTargetState)
.on 'get', (callback) =>
@handleReturnPromise(device.getPosition(), callback, @getLockCurrentState)

# opposite of target position getter
@getService(Service.LockMechanism)
.getCharacteristic(Characteristic.LockCurrentState)
.on 'get', (callback) =>
@handleReturnPromise(device.getPosition(), callback, @getLockCurrentState)

device.on 'position', (position) =>
@getService(Service.LockMechanism)
.setCharacteristic(Characteristic.LockCurrentState, @getLockCurrentState(position))

getLockCurrentState: (position) =>
if position == 'up'
return Characteristic.LockCurrentState.UNSECURED
else if position == "down"
return Characteristic.LockCurrentState.SECURED
else
# stopped somewhere in between
return Characteristic.LockCurrentState.UNKNOWN
else
callback()

getCurrentState: (position) ->
assert position in ['up', 'down', 'stopped']
return switch position
when 'up' then Characteristic.CurrentDoorState.OPEN
when 'down' then Characteristic.CurrentDoorState.CLOSED
when 'stopped' then Characteristic.CurrentDoorState.STOPPED

getTargetPosition: (state) ->
return switch state
when Characteristic.TargetDoorState.OPEN then 'up'
when Characteristic.TargetDoorState.CLOSED then 'down'
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"grunt-mocha-test": "0.8.1",
"blanket": "1.1.5",
"coffee-errors": "0.8.4",
"mocha": "1.17.0"
"mocha": "1.17.0",
"chai": "3.5.0"
},
"peerDependencies": {
"pimatic": "0.8.*"
Expand Down
120 changes: 120 additions & 0 deletions test/shutter-test.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
grunt = require 'grunt'
assert = require 'assert'
Promise = require 'bluebird'

env =
logger:
debug: (stmt) ->
grunt.log.writeln stmt
error: (stmt) ->
grunt.log.writeln stmt
ShutterAccessory = require("../accessories/shutter")(env)
hap = require 'hap-nodejs'
Service = hap.Service
Characteristic = hap.Characteristic

class TestShutter extends require('events').EventEmitter
id: "testshutter-id"
name: "testshutter"
config: {}

_position: null

getPosition: ->
Promise.resolve(@_position)

firePositionChange: (position) ->
@_position = position
@emit 'position', position

moveUp: ->
@_position = "up"
return Promise.resolve()

moveDown: ->
@_position = "down"
return Promise.resolve()

stop: ->
@_position = "stopped"
return Promise.resolve()

describe "shutter", ->

device = null
accessory = null

beforeEach ->
device = new TestShutter()
accessory = new ShutterAccessory(device)

describe "getting Characteristic.CurrentDoorState", ->

it "should return CLOSED when postion is down", ->
device._position = 'down'
accessory.getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.CurrentDoorState)
.getValue((error, value) ->
assert error is null
assert value is Characteristic.CurrentDoorState.CLOSED
)

it "should return STOPPED when postion is stopped", ->
device._position = 'stopped'
accessory.getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.CurrentDoorState)
.getValue((error, value) ->
assert error is null
assert value is Characteristic.CurrentDoorState.STOPPED
)

describe "changing position", ->

it "should change Characteristic.CurrentDoorState", ->
device.firePositionChange('down')
accessory.getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.CurrentDoorState)
.getValue((error, value) ->
assert error is null
assert value is Characteristic.CurrentDoorState.CLOSED
)

describe "changing Characteristic.TargetDoorState", ->

it "should call moveUp() when set to OPEN", ->
accessory.getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.TargetDoorState)
.setValue(Characteristic.TargetDoorState.OPEN)
assert device._position is 'up'

it "should call moveDown() when set to CLOSED", ->
accessory.getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.TargetDoorState)
.setValue(Characteristic.TargetDoorState.CLOSED)
assert device._position is 'down'

it "should call stop() when set to OPEN twice", ->
accessory.getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.TargetDoorState)
.setValue(Characteristic.TargetDoorState.OPEN)
accessory.getService(Service.GarageDoorOpener)
.getCharacteristic(Characteristic.TargetDoorState)
.setValue(Characteristic.TargetDoorState.OPEN)
assert device._position is 'stopped'

describe "getCurrentState", ->

it "should return correct value", ->
assert accessory.getCurrentState('up') is Characteristic.CurrentDoorState.OPEN
assert accessory.getCurrentState('down') is Characteristic.CurrentDoorState.CLOSED
assert accessory.getCurrentState('stopped') is Characteristic.CurrentDoorState.STOPPED

it "should trow an error when used with wrong value", ->
fn = () -> accessory.getCurrentState('bla')
require('chai').expect(fn).to.throw(assert.AssertionError)

describe "getTargetPosition", ->

it "should return correct value", ->
assert accessory.getTargetPosition(Characteristic.TargetDoorState.OPEN) is 'up'
assert accessory.getTargetPosition(Characteristic.TargetDoorState.CLOSED) is 'down'

0 comments on commit 95740ca

Please sign in to comment.