Skip to content

Instantly share code, notes, and snippets.

@markusl
Last active August 29, 2015 14:13
Show Gist options
  • Save markusl/8b8ad0d8d7c49facb09e to your computer and use it in GitHub Desktop.
Save markusl/8b8ad0d8d7c49facb09e to your computer and use it in GitHub Desktop.
Houm.io temperature collection with Enocean STM-330
Bacon = require('baconjs')
serialport = require("serialport")
sleep = require('sleep')
WebSocket = require('ws')
winston = require('winston')
Keen = require('keen.io')
Keen = Keen or
configure: (e) ->
@_cf = e
addEvent: (e, t, n, i) ->
@_eq = @_eq or []
@_eq.push([e, t, n, i])
setGlobalProperties: (e) ->
@_gp = e
onChartsReady: (e) ->
@_ocrq = @_ocrq or []
@_ocrq.push(e)
winston.remove(winston.transports.Console)
winston.add(winston.transports.Console, { timestamp: ( -> new Date() ) })
console.log = winston.info
horselightsServer = process.env.HORSELIGHTS_SERVER || "ws://localhost:3000"
horselightsSitekey = process.env.HORSELIGHTS_SITEKEY || "devsite"
horselightsEnOceanDeviceFile = process.env.HORSELIGHTS_ENOCEAN_DEVICE_FILE || "/dev/cu.usbserial-FTXMJM92"
console.log "Using HORSELIGHTS_SERVER=#{horselightsServer}"
console.log "Using HORSELIGHTS_SITEKEY=#{horselightsSitekey}"
console.log "Using HORSELIGHTS_ENOCEAN_DEVICE_FILE=#{horselightsEnOceanDeviceFile}"
exit = (msg) ->
console.log msg
process.exit 1
enOceanSerialBuffer = null
onEnOceanTimeoutObj = null
socket = null
pingId = null
enOceanData = new Bacon.Bus()
writeReady = new Bacon.Bus()
enOceanWriteAndDrain = (data, callback) ->
enOceanSerial.write data, (err, res) ->
enOceanSerial.drain callback
enOceanData
.zip(writeReady, (d, w) -> d)
.flatMap (d) -> Bacon.fromNodeCallback(enOceanWriteAndDrain, d)
.onValue (err) ->
sleep.usleep(0.01*1000000)
writeReady.push(true)
enOceanStartByte = 0x55
enOceanHeaderLength = 6
enOceanIsDataValid = (cmd) ->
if cmd.length < 7 then return false
if cmd[0] != enOceanStartByte then return false
if !enOceanIsDataLengthValid(cmd) then return false
true
enOceanIsDataLengthValid = (data) ->
dataLen = data[1] * 0xff + data[2]
optLen = data[3]
totalLen = enOceanHeaderLength + dataLen + optLen + 1
data.length == totalLen
onSocketOpen = ->
console.log "Connected to #{horselightsServer}"
enOceanSerial.on 'data', onEnOceanSerialData
pingId = setInterval ( -> socket.ping(null, {}, false) ), 3000
publish = JSON.stringify { command: "publish", data: { sitekey: horselightsSitekey, vendor: "enocean" } }
socket.send(publish)
console.log "Sent message:", publish
onSocketClose = ->
clearInterval pingId
exit "Disconnected from #{horselightsServer}"
onSocketMessage = (s) ->
console.log "Received message:", s
try
message = JSON.parse s
enOceanData.push message.data
keen = Keen.configure(
projectId: ".."
writeKey: ".."
readKey: ".."
)
send = (type, value, sensor) ->
keen.addEvent "sensors",
value: value
type: type
sensor: sensor,
(err, res) ->
if err
console.log "keen " + err
else
console.log "Sent: " + value + " type: " + type
return
return
tempSensor1 = [1, 130, 87, 42]
actions = (sensorId) -> [
{
sensor: tempSensor1
handle: (data) ->
temperature = (255 - data[9]) / 255 * 40
send "temperature", temperature, sensorId
return
}
]
# Lots of LOL
arrayEqual = (ar1, ar2) ->
JSON.stringify(ar1) is JSON.stringify(ar2)
emit = (data) ->
sensor = data[11..14]
actions(sensor).forEach (action) ->
action.handle data if arrayEqual sensor, action.sensor
return
return
onEnOceanSerialData = (data) ->
if data[0] == enOceanStartByte && enOceanSerialBuffer == null
onEnOceanTimeoutObj = setTimeout onEnOceanTimeout, 100
if enOceanIsDataValid data
s = JSON.stringify { command: "enoceandata", data: data }
socket.send s
console.log "Sent message:", s
enOceanSerialBuffer = null
clearTimeout onEnOceanTimeoutObj
else
enOceanSerialBuffer = data.slice 0, data.length
else if enOceanSerialBuffer != null
enOceanSerialBuffer = Buffer.concat [enOceanSerialBuffer, data]
if enOceanIsDataValid enOceanSerialBuffer
emit enOceanSerialBuffer
s = JSON.stringify { command: "enoceandata", data: enOceanSerialBuffer }
socket.send s
console.log "Sent message:", s
enOceanSerialBuffer = null
clearTimeout onEnOceanTimeoutObj
onEnOceanTimeout = () ->
enOceanSerialBuffer = null
clearTimeout onEnOceanTimeoutObj
onEnOceanSerialOpen = ->
console.log 'Serial port opened:', horselightsEnOceanDeviceFile
writeReady.push(true)
socket = new WebSocket(horselightsServer)
socket.on 'open', onSocketOpen
socket.on 'close', onSocketClose
socket.on 'error', exit
socket.on 'ping', -> socket.pong()
socket.on 'message', onSocketMessage
onEnOceanSerialError = (err) ->
exit "An error occurred in EnOcean serial port: #{err}"
onEnOceanSerialClose = (err) ->
exit "EnOcean serial port closed, reason: #{err}"
enOceanSerialConfig = { baudrate: 57600, parser: serialport.parsers.raw }
enOceanSerial = new serialport.SerialPort horselightsEnOceanDeviceFile, enOceanSerialConfig, true
enOceanSerial.on "open", onEnOceanSerialOpen
enOceanSerial.on "close", onEnOceanSerialClose
enOceanSerial.on "error", onEnOceanSerialError
@markusl
Copy link
Author

markusl commented Jan 19, 2015

To measure room temperatures (the slightly expensive, but the internet-of-things, way)

  • Install Houm.io light configuration system in your house. See http://houm.io
  • Order Enocean STM-330 wireless temperature sensor for 0-40C range. See http://fi.farnell.com/enocean/stm-330/module-wireless-temp-sensor-868mhz/dp/2134190
  • Log in to your Houmio central unit via ssh
  • npm install keen.io
  • Register to keen.io and create a new project for monitoring
  • Update ~/horselights-site-enocean/site.coffee match the site.coffee above
  • Configure keen.io keys
  • Restart node, check logs to see if everything is okay
  • Configure some keen.io dashboards for displaying the data
  • Maybe press the LRN button in the STM-330 to get first measurement

Enjoy the user manual of STM-330: http://www.element14.com/community/servlet/JiveServlet/previewBody/67254-102-2-292067/EnOcean%20Temperature%20Sensor%20User%20Manual.pdf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment