Commit c7f472eb authored by Peter Müller's avatar Peter Müller

Created new Pimatic plugin

parents
node_modules
doc/*
.js
This diff is collapsed.
# Pimatic PCF8574 Plugin
(c) 2017 Peter Müller <peter@crycode.de>
module.exports = {
title: "PCF8574 device config schemes"
PCF8574IC: {
title: "PCF8574IC config options"
type: "object"
properties:
address:
description: "The address of the IC in hex or dec. (0x00 to 0xFE)"
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"
default: 17
pollingInterval:
description: "The interval in milliseconds polling is done to detect input changes, if polling is used. Lower time means higher bus load."
type: "integer"
default: 200
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
}
PCF8574ContactSensor: {
title: "PCF8574ContactSensor config options"
type: "object"
extensions: ["xLink", "xClosedLabel", "xOpenedLabel"]
properties:
PCF8574IC:
description: "The ID of PCF8574IC device to which this device is connected. This device must be created first."
type: "string"
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: {
title: "PCF8574Switch config options"
type: "object"
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."
type: "string"
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"
required: false
}
}
{
"name": "pimatic-pcf8574",
"description": "Plugin for Pimatic to use a PCF8574/PCF8574A/PCF8574P portexpander IC.",
"author": "Peter Müller <peter@crycode.de> (https://crycode.de)",
"keywords": [
"pcf8574",
"pcf8574a",
"pcf8574p",
"i2c",
"portexpander",
"raspberry pi",
"gpio",
"pimatic"
],
"main": "pcf8574",
"files": [
"pcf8574.coffee",
"README.md",
"pcf8574-config-schema.coffee"
],
"version": "0.0.1",
"homepage": "https://crycode.de",
"private": true,
"repository": {
"type": "git",
"url": "https://git.cryhost.de/crycode/pimatic-pcf8574.git"
},
"configSchema": "pcf8574-config-schema.coffee",
"dependencies": {
"pcf8574": "^1.0.1",
"i2c-bus": "^1.2.2"
},
"peerDependencies": {
"pimatic": "0.9.*"
},
"engines": {
"node": ">4.x.x",
"npm": ">1.1.x"
}
}
module.exports = {
title: "PCF8574 config options"
type: "object"
properties:
i2cBusNr:
description: "Number of the used I2C-Bus"
type: "number"
default: 1
debug:
description: "Enable debug output"
type: "boolean"
default: false
}
###
# Pimatic PCF8574 Plugin
#
# Plugin for Pimatic to use a PCF8574/PCF8574A/PCF8574P portexpander IC.
#
###
module.exports = (env) ->
# Require the bluebird promise library
Promise = env.require 'bluebird'
# Require the [cassert library](https://github.com/rhoot/cassert).
assert = env.require 'cassert'
EventEmitter = require 'events'
PCF8574 = require('pcf8574').PCF8574
env.i2cBus = require('i2c-bus').openSync(1) if !env.i2cBus?
# ###PimaticPCF8574 class
class PimaticPCF8574 extends env.plugins.Plugin
# ####init()
# The `init` function is called by the framework to ask your plugin to initialise.
#
# #####params:
# * `app` is the [express] instance the framework is using.
# * `framework` the framework itself
# * `config` the properties the user specified as config for your plugin in the `plugins`
# section of the config.json file
#
#
init: (app, @framework, @config) =>
env.logger.debug('config', @config)
deviceConfigDef = require("./device-config-schema")
@framework.deviceManager.registerDeviceClass('PCF8574IC', {
configDef: deviceConfigDef.PCF8574IC,
createCallback: (deviceConfig, lastState) =>
device = new PCF8574IC(deviceConfig, @framework)
return device
})
@framework.deviceManager.registerDeviceClass('PCF8574ContactSensor', {
configDef: deviceConfigDef.PCF8574ContactSensor,
createCallback: (deviceConfig, lastState) =>
device = new PCF8574ContactSensor(deviceConfig, lastState)
return device
})
@framework.deviceManager.registerDeviceClass('PCF8574Switch', {
configDef: deviceConfigDef.PCF8574Switch,
createCallback: (deviceConfig, lastState) =>
device = new PCF8574Switch(deviceConfig, lastState)
return device
})
# Create a instance of my plugin
pimaticPCF8574 = new PimaticPCF8574
# ###PCF8574IC class
class PCF8574IC extends env.devices.Device
constructor: (@config, @framework) ->
@id = @config.id
@name = @config.name
super()
@pollingInterval = null
# parse and check the address
@address = parseInt @config.address
assert @address >= 0 and @address <= 255, 'The address must be between 0x00 (0) and 0xFF (255)'
# parse and check initialState
if @config.initialState == 'true'
initialState = 0xff
else if @config.initialState == 'false'
initialState = 0x00
else
initialState = parseInt @config.initialState
assert initialState >= 0 and initialState <= 255, "The initial state must be 'true', 'false' or between 0x00 (0) and 0xFF (255)"
# init the PCF8574
env.logger.debug "init #{@id} with address #{@address}"
@ic = new PCF8574(env.i2cBus, @address, initialState)
# input change detection
if @config.inputChangeDetection == 'interrupt'
assert typeof @config.interruptPin is 'number'
@ic.enableInterrupt @config.interruptPin
else if @config.inputChangeDetection == 'polling'
assert typeof @config.pollingInterval is 'number'
@pollingInterval = setInterval ()=>
@ic.doPoll
, @config.pollingInterval
destroy: ->
super()
env.logger.debug "destroy #{@id}"
clearInterval @pollingInterval
@ic.removeAllListeners()
@ic.disableInterrupt()
# ###PCF8574ContactSensor class
class PCF8574ContactSensor extends env.devices.ContactSensor
constructor: (@config, lastState) ->
env.logger.debug('PCF8574ContactSensor', @config, lastState)
@id = @config.id
@name = @config.name
@_contact = lastState?.contact?.value or false
# 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"
# check the pin number
assert typeof @config.pinNumber is 'number'
assert 7 >= @config.pinNumber >= 0, "The pinNumber must be between 0 and 7"
# set pin as input
@pcf8574ic.ic.inputPin @config.pinNumer, @config.inverted
# handle input changes
@pcf8574ic.ic.on('input', inputListener = (data) =>
return if data.pin != @config.pinNumber
env.logger.debug "#{@id} input event", data
@_setContact data.value
)
@on 'destroy', () => @pcf8574ic.ic.removeListener('input', inputListener)
super()
destroy: ->
super()
class PCF8574Switch extends env.devices.PowerSwitch
constructor: (@config, lastState) ->
env.logger.debug('PCF8574ContactSensor', @config, lastState)
@id = @config.id
@name = @config.name
# 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"
# check the pin number
assert typeof @config.pinNumber is 'number'
assert 7 >= @config.pinNumber >= 0, "The pinNumber must be between 0 and 7"
# define
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
super()
getState: () ->
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
@pcf8574ic.ic.setPin(@config.pinNumber, state).then( () =>
@_setState(state)
)
# ###Finally
# return it to the framework.
return pimaticPCF8574
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