Commit 8b4082e8 authored by Peter Müller's avatar Peter Müller

Made Switch and ContactSensor devices fully functional

parent c7f472eb
......@@ -6,14 +6,12 @@ module.exports = {
type: "object"
properties:
address:
description: "The address of the IC in hex or dec. (0x00 to 0xFE)"
description: "The address of the IC in hex or dec (0x00 to 0xFF, e.g. 0x38). If no IC with this address is connected, an input/output error will be thrown."
type: "string"
default: "0x38"
inputChangeDetection:
description: "How to detect changes on input pins. Select 'interrupt' to use a Raspberry Pi GPIO pin or 'polling' to poll for changes. If you don't need to detect input changes select 'none'."
type: "string"
enum: ["none", "interrupt", "polling"]
default: "none"
interruptPin:
description: "Pin number of the Raspberry Pi GPIO pin, if the interrupt is used. This must be the BCM pin number."
type: "integer"
......@@ -25,7 +23,7 @@ module.exports = {
initialState:
description: "The initial state of the pins of this IC. You can set a decimal or hexadecimal bitmask to define each pin seprately, or use true/false for all pins at once."
type: "string"
default: true
default: "true"
}
PCF8574ContactSensor: {
......@@ -34,17 +32,16 @@ module.exports = {
extensions: ["xLink", "xClosedLabel", "xOpenedLabel"]
properties:
PCF8574IC:
description: "The ID of PCF8574IC device to which this device is connected. This device must be created first."
description: "The ID of PCF8574IC device to which this device is connected. This device must be created first. If the PCF8574IC device gets deleted, this PCF8574ContactSensor device will be deleted too."
type: "string"
enum: []
pinNumber:
description: "The pin number at the PCF8574 IC."
type: "integer"
enum: [0,1,2,3,4,5,6,7]
default: 0
inverted:
description: "If this pin should be handled inverted or not. (inverted means true=low and false=high)"
type: "boolean"
default: false
}
PCF8574Switch: {
......@@ -53,17 +50,16 @@ module.exports = {
extensions: ["xConfirm", "xLink", "xOnLabel", "xOffLabel"]
properties:
PCF8574IC:
description: "The ID of PCF8574IC device to which this device is connected. This device must be created first."
description: "The ID of PCF8574IC device to which this device is connected. This device must be created first. If the PCF8574IC device gets deleted, this PCF8574Switch device will be deleted too."
type: "string"
enum: []
pinNumber:
description: "The pin number at the PCF8574 IC."
type: "integer"
enum: [0,1,2,3,4,5,6,7]
default: 0
inverted:
description: "If this pin should be handled inverted or not. (inverted means true=low and false=high)"
type: "boolean"
default: false
defaultState:
description: "State to set on startup, if not given, last state will be restored"
type: "boolean"
......
......@@ -27,7 +27,7 @@
},
"configSchema": "pcf8574-config-schema.coffee",
"dependencies": {
"pcf8574": "^1.0.1",
"pcf8574": "^1.0.2",
"i2c-bus": "^1.2.2"
},
"peerDependencies": {
......
......@@ -12,12 +12,17 @@ module.exports = (env) ->
# Require the [cassert library](https://github.com/rhoot/cassert).
assert = env.require 'cassert'
_ = env.require 'lodash'
EventEmitter = require 'events'
PCF8574 = require('pcf8574').PCF8574
env.i2cBus = require('i2c-bus').openSync(1) if !env.i2cBus?
# enum of devices which need a PCF8574IC device
pcf8574Devices = ['PCF8574ContactSensor', 'PCF8574Switch']
# ###PimaticPCF8574 class
class PimaticPCF8574 extends env.plugins.Plugin
......@@ -46,14 +51,14 @@ module.exports = (env) ->
@framework.deviceManager.registerDeviceClass('PCF8574ContactSensor', {
configDef: deviceConfigDef.PCF8574ContactSensor,
createCallback: (deviceConfig, lastState) =>
device = new PCF8574ContactSensor(deviceConfig, lastState)
device = new PCF8574ContactSensor(deviceConfig, lastState, @framework)
return device
})
@framework.deviceManager.registerDeviceClass('PCF8574Switch', {
configDef: deviceConfigDef.PCF8574Switch,
createCallback: (deviceConfig, lastState) =>
device = new PCF8574Switch(deviceConfig, lastState)
device = new PCF8574Switch(deviceConfig, lastState, @framework)
return device
})
......@@ -74,7 +79,7 @@ module.exports = (env) ->
assert @address >= 0 and @address <= 255, 'The address must be between 0x00 (0) and 0xFF (255)'
# parse and check initialState
if @config.initialState == 'true'
if not @config.initialState? or @config.initialState == 'true'
initialState = 0xff
else if @config.initialState == 'false'
initialState = 0x00
......@@ -89,16 +94,30 @@ module.exports = (env) ->
# input change detection
if @config.inputChangeDetection == 'interrupt'
assert typeof @config.interruptPin is 'number'
env.logger.debug "#{@id} using interrupt GPIO pin #{@config.interruptPin}"
@ic.enableInterrupt @config.interruptPin
else if @config.inputChangeDetection == 'polling'
assert typeof @config.pollingInterval is 'number'
@pollingInterval = setInterval ()=>
@ic.doPoll
, @config.pollingInterval
env.logger.debug "#{@id} polling for input changes every #{@config.pollingInterval} ms"
@pollingInterval = setInterval @ic.doPoll, @config.pollingInterval
# add to the other device classes configs
_.forEach pcf8574Devices, (device) =>
@framework.deviceManager.deviceClasses[device].configDef.properties.PCF8574IC.enum = _.union @framework.deviceManager.deviceClasses[device].configDef.properties.PCF8574IC.enum, [@id]
destroy: ->
super()
env.logger.debug "destroy #{@id}"
super()
# remove all devices which belongs to this device
_.forEach @framework.deviceManager.getDevices(), (device) =>
if device.config.PCF8574IC is @id
env.logger.info "removing device #{device.config.id} because it belongs to #{@id}"
@framework.deviceManager.removeDevice device.config.id
# remove from the other device classes configs
_.forEach pcf8574Devices, (device) =>
@framework.deviceManager.deviceClasses[device].configDef.properties.PCF8574IC.enum = _.pull @framework.deviceManager.deviceClasses[device].configDef.properties.PCF8574IC.enum, @id
clearInterval @pollingInterval
@ic.removeAllListeners()
......@@ -106,7 +125,7 @@ module.exports = (env) ->
# ###PCF8574ContactSensor class
class PCF8574ContactSensor extends env.devices.ContactSensor
constructor: (@config, lastState) ->
constructor: (@config, lastState, @framework) ->
env.logger.debug('PCF8574ContactSensor', @config, lastState)
@id = @config.id
......@@ -122,7 +141,7 @@ module.exports = (env) ->
assert 7 >= @config.pinNumber >= 0, "The pinNumber must be between 0 and 7"
# set pin as input
@pcf8574ic.ic.inputPin @config.pinNumer, @config.inverted
@pcf8574ic.ic.inputPin @config.pinNumber, @config.inverted? or false
# handle input changes
@pcf8574ic.ic.on('input', inputListener = (data) =>
......@@ -140,12 +159,14 @@ module.exports = (env) ->
super()
class PCF8574Switch extends env.devices.PowerSwitch
constructor: (@config, lastState) ->
constructor: (@config, lastState, @framework) ->
env.logger.debug('PCF8574ContactSensor', @config, lastState)
@id = @config.id
@name = @config.name
@initializing = true
# get the PCF8574IC instance and check it
@pcf8574ic = @framework.deviceManager.getDeviceById(@config.PCF8574IC)
assert @pcf8574ic? and @pcf8574ic instanceof PCF8574IC, "No PCF8574IC device found with the given ID"
......@@ -154,27 +175,45 @@ module.exports = (env) ->
assert typeof @config.pinNumber is 'number'
assert 7 >= @config.pinNumber >= 0, "The pinNumber must be between 0 and 7"
# define
# define the current state
if @config.defaultState?
@_state = @config.defaultState
else
@_state = lastState?.state?.value or false
# set pin as output
@pcf8574ic.ic.outputPin @config.pinNumer, @config.inverted, @_state
@pcf8574ic.ic.outputPin @config.pinNumber, @config.inverted? or false, @_state
.then ()=>
env.logger.debug "#{@id} output pin initialized"
@initializing = false
@emit 'initialized'
.catch ()=>
env.logger.error "Error setting pin as output (device: #{@id}, pcf8574ic: #{@pcf8574ic.id})"
super()
getState: () ->
if @_state? then Promise.resolve @_state
@_state = @pcf8574ic.ic.getPinValue @config.pinNumber
destroy: ->
super()
Promise.resolve @_state
getState: () ->
# delay getState if initialization of this pin isn't done
if @initializing
new Promise (resolve, reject) =>
@on('initialized', initializedListener = () =>
if @_state? then resolve @_state
@_state = @pcf8574ic.ic.getPinValue @config.pinNumber
resolve @_state
@removeListener 'initialized', initializedListener
)
else
if @_state? then Promise.resolve @_state
@_state = @pcf8574ic.ic.getPinValue @config.pinNumber
Promise.resolve @_state
changeStateTo: (state) ->
assert state is on or state is off
env.logger.debug "#{@id} changeStateTo #{state}"
@pcf8574ic.ic.setPin(@config.pinNumber, state).then( () =>
@_setState(state)
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment