When I installed my openHAB controller I looked for a binding for the NESS/ELK but was not able to find one. There are some active conversations on the forums but no available bindings, so I decided to try my hand at getting something going. I'm using a USB to Serial adaptor plugged from the serial port of my home alarm directly in to my Raspberry Pi running openHAB2.
OpenHAB Serial Binding
First thing to do is to install and configure the serial binding. I have already covered this in an older post.
Reading Zone States
The easiest thing to start with is to read the zone state from the NESS alarm controller. With serial configured this is simply a matter of reading the serial input data and interpreting the commands based on the well documented serial protocol for the controller.
Here are the files I have used to mirror the state of the motion sensors on my home alarm by reading the zone states:
alarm.items
Switch AlarmUpdate "Alarm Update" { serial="/dev/ttyUSB0@115200" }
String AlarmMessage "Alarm Message" { serial="/dev/ttyUSB0@115200" }
Group gAlarm_PIR
Contact Alarm_PIR_Zone1 (gAlarm_PIR)
Contact Alarm_PIR_Zone2 (gAlarm_PIR)
Contact Alarm_PIR_FF_MasterBedroom "Master Bedroom Motion Sensor [%s]" (gAlarm_PIR)
Contact Alarm_PIR_Zone4 (gAlarm_PIR)
Contact Alarm_PIR_Nursery "Nursery Motion Sensor [%s]" (gAlarm_PIR)
Contact Alarm_PIR_FF_Stairs "Stairs Motion Sensor [%s]" (gAlarm_PIR)
Contact Alarm_PIR_GF_Lounge "Lounge Motion Sensor [%s]" (gAlarm_PIR)
Contact Alarm_PIR_GF_Stairs "Basement Stairs Motion Sensor [%s]" (gAlarm_PIR)
init.rules
rule "Init"
when
System started
then
// let openHAB settle and give other rules the chance to fire
createTimer(now.plusSeconds(180)) [|
gAlarm_PIR?.members.forEach[g |
g.postUpdate(CLOSED)
]
end
alarm.rules
import java.lang.Integer.*
val AlarmMemoryMessage = "AM"
val ZoneChangedMessage = "ZC"
val NetworkAdapterPing = "XK"
val ZoneStatusTable_NormalUnconfigured = "0"
val ZoneStatusTable_NormalOpen = "1"
val ZoneStatusTable_NormalEOL = "2"
val ZoneStatusTable_NormalShort = "3"
val ZoneStatusTable_TroubleOpen = "5"
val ZoneStatusTable_TroubleEOL = "6"
val ZoneStatusTable_TroubleShort = "7"
val ZoneStatusTable_ViolatedOpen = "9"
val ZoneStatusTable_ViolatedEOL = "A"
val ZoneStatusTable_ViolatedShort = "B"
val ZoneStatusTable_BypassedOpen = "D"
val ZoneStatusTable_BypassedEOL = "E"
val ZoneStatusTable_BypassedShort = "F"
rule "Interpret Alarm Command"
when
Item AlarmMessage received update
then
var lines = AlarmMessage.state.toString.split('\n')
lines.forEach[line |
if(line.length > 6) {
var messageLength = line.substring(0,2)
var commandType = line.substring(2,4)
var checksum = line.substring(line.length-3, line.length-1)
//logInfo("alarm", "Message:" + line + " Len:" + messageLength + " Command:" + commandType)
if(commandType == AlarmMemoryMessage) {
var alarmMemoryAreas = line.substring(4, 12)
}
else if(commandType == ZoneChangedMessage) {
else if(commandType == ZoneChangedMessage) {
var int zoneNumber = Integer::parseInt(line.substring(4,7))
var zoneStatus = line.substring(7,8)
//logInfo("alarm", "Zone:" + zoneNumber + " Status:" + zoneStatus)
//logInfo("alarm", "gAlarm_PIR:" + gAlarm_PIR.members.filter(g | g.name == "Alarm_PIR_GF_Lounge").toString)
var zone = Alarm_PIR_GF_Lounge
var state = CLOSED
// case statement setting the zone item from the message received
switch zoneNumber {
case zoneNumber == 1 : zone = Alarm_PIR_Zone1
case zoneNumber == 2 : zone = Alarm_PIR_Zone2
case zoneNumber == 3 : zone = Alarm_PIR_FF_MasterBedroom
case zoneNumber == 4 : zone = Alarm_PIR_Zone4
case zoneNumber == 5 : zone = Alarm_PIR_FF_Nursery
case zoneNumber == 6 : zone = Alarm_PIR_FF_Stairs
case zoneNumber == 7 : zone = Alarm_PIR_GF_Lounge
case zoneNumber == 8 : zone = Alarm_PIR_GF_Stairs
}
// set the state of the zone from the message received
if(zoneStatus == ZoneStatusTable_NormalUnconfigured) {
state = CLOSED
}
else if(zoneStatus == ZoneStatusTable_NormalOpen) {
state = CLOSED
}
else if(zoneStatus == ZoneStatusTable_NormalEOL) {
state = CLOSED
}
else if(zoneStatus == ZoneStatusTable_TroubleOpen) {
state = OPEN
}
else if(zoneStatus == ZoneStatusTable_TroubleEOL) {
state = OPEN
}
else if(zoneStatus == ZoneStatusTable_TroubleShort) {
state = OPEN
}
else if(zoneStatus == ZoneStatusTable_ViolatedOpen) {\
state = OPEN
}
else if(zoneStatus == ZoneStatusTable_ViolatedEOL) {
state = OPEN
}
else if(zoneStatus == ZoneStatusTable_ViolatedShort) {
state = OPEN
}
else {
state = CLOSED
}
postUpdate(zone, state)
}
else if(commandType == NetworkAdapterPing) {
}
else {
//logInfo("alarm", "No code match")
}
}
]
end
Add the following lines to your sitemap:
Frame label="Ground Floor Stairs" {
Text item=Alarm_PIR_GF_Stairs icon="motion"
}
Testing
View the sitemap and we should see the motion sensor updates coming throughTest the sitemap is working as expected with the command:
tail -f /var/log/openhab2/openhab.log -f /var/log/openhab2/events.log tail -f /var/log/openhab2/openhab.log -f /var/log/openhab2/events.log
If everything is working as expected you should see updates coming through:
==> /var/log/openhab2/events.log <==
2017-04-03 13:44:24.818 [GroupItemStateChangedEvent] - gAlarm_PIR changed from CLOSED to UNDEF through Alarm_PIR_GF_Lounge
2017-04-03 13:44:28.191 [ItemStateChangedEvent ] - AlarmMessage changed from 0AZC007900C2
1EAS000000000111111100000000000F
0CAM000000007F
to 0AZC007200C9
1EAS000000001111111100000000000E
0CAM000000007F
2017-04-03 13:46:26.579 [ItemStateChangedEvent ] - Alarm_PIR_GF_Lounge changed from OPEN to CLOSED
This looks really good. Do you think it will work with the NESS DX8Deluxe panel?
ReplyDeleteNot sure, does the DX8 have a serial communication port?
DeleteI've only worked with the M1 which is a home automation controller so it has these features built in by default
Super old post I know but are you still using this?
ReplyDeleteI have the M1XEP and was hoping to do something similar using TCP.
Yeah it was working like a charm till I decommissioned it a couple weeks ago. Was a great setup.
DeleteSo was I, I had the M1XEP but couldn't work out for the life of me how to get it set up. I assumed I had a dud, which is why I went with the serial setup.
Oh you decommissioned it? :(
DeleteI managed to get it to work to a degree with M1XEP.
Sometimes it works sometimes it doesn't though recently I have noticed these in the logs
Cannot parse input 16XK45251762606200110066
to match command 0 on item AlarmUpdate
It has stopped processing as a result. :(
and then just like that it started working again..
DeleteHave you tried un-commenting the log messages to give some more info on where it's breaking?
DeleteI'm guessing your setup is different to mine and so there is probably something unaccounted for in my substring logic.
Yup thats the next step, i just started fixing up the code to it stopped the warnings and moved variables like so;
ReplyDeletevar alarmMemoryAreas = null
var checksum = null
Will wait for it to happen again and start debugging.
Also interesting to note, you can not connect to the panel via the secure port when using the non-secure port with openhab. I am usign the TCP Binding
Switch AlarmUpdate "Alarm Update" { tcp=">[192.168.xxx.xxx:2101:'REGEX((.*))']" }
String AlarmMessage "Alarm Message" { tcp=">[192.168.xxx.xxx:2101:'REGEX((.*))']" }