Several people have asked me about the Iris Door Sensor, and I haven't had much interest. My battery operated switch can do this and is totally programmable, but I figured, "What the heck." I went ahead and got one when I went to Lowe's to get another smart switch.
Now, this is one of those projects that will go on and on as I try to get it to work. Meanwhile, these are the first steps.
This is what one looks like:
I don't think Lowe's will mind that I stole the picture from their site. First thing I did was open it up and look inside:
The spot on the left is for the battery and there's a switch for joining it to a network. I tried to get it to join with my Iris network, but it didn't work, so being me, I took it apart. Here's the circuit board removed from the casing:
Not much on this side, so flipping it over:
This is a side view of the magnet it comes with:
Remember those little extrusions on the magnet case; I'll talk about them later. I got out the magnifying glass and took a look at the active components, the list isn't very long:
Battery - CR2 Lithium, 3V
Magnetic Reed Switch - Hamlin 59050-1-S-00-0 Normally Open
Magnet off center to be near the reed switch.
Antenna - Antenova a6150
Processor - Silicon Labs Ember 250
Temperature Sensor - Microchip MCP9801M
Nice set of high quality components. this thing is put together pretty well. The important part is the Ember 250 SOC (system on a chip) that runs the device. These are made specifically for ZigBee and are very nice devices; to bad the product comes from Alertme. They always ignore the ZigBee spec and go their own way in critical areas. This is going to make it tough to hack into.
I put together some code and turned off my normal network to keep down interference and gave it a try. I couldn't get it to join properly, but I was able to see it trying. Since this is an Alertme device, it has significant API differences from the ZigBee spec, so I started trying things. I got it to join eventually by a non-reproducible accident and saw it running.
The problem is that I can't get that to happen again. When it was working, I noticed that it was really sensitive to where the magnet was, so I looked at the reed switch specs and it's only got a 7 mm range. That means that the magnet needs to be right next to the sensor for it to close. The FAQ for the switch on the Lowe's site even talks about that. They blame it on polarity when it's really the fact that the magnet needs to be so close to the sensor; like polarity would matter on a reed switch. A neo magnet might make the range longer, but it wouldn't be as pretty.
Some of us may have problems with that on installation; seven millimeters is not very much, especially when it's about 4 mm from the switch to the case. Do you see why they have those little protusions on the case now? It gives some spacing and a little place for the magnet to rub on the sensor case when the door closes. Strange way to build it, but I'm not an engineer.
About how it works: When the battery is first inserted, it starts sending Device Announce messages and continues this until I answer it with an Active Endpoint Request (which it hasn't answered yet). When it gets the Active Endpoint, it sends a Match Descriptor request and I answer it with a Match Descriptor Response tailored to it. Then, it sends one of the odd Alertme messages to profile 0xC216 that I've seen before from Alertme devices.
This is the point where, with the smart switches, I send a set of canned responses back and the device would join. The door switch doesn't join, It will stay around for a while trying, but will eventually give up and move on to another channel to look for another controller.
The time I got it to join, it started sending messages to cluster 0x500, the security device cluster. While it was doing that I could actually see the switch change state because the values it sent back were changing based on magnet location. However, it was erratic and extremely slow at sensing the magnet. There was some LED flashing on the device that seemed to correspond to the position of the magnet, but I couldn't be sure.
I'm going to post the code I'm working with as it is right now, but it's not pretty. I hacked up a piece of code I developed to break into another ZigBee device and am making experimental changes to it trying to understand this door switch.
If you grab it, there's an 'if' statement up at the top where I get the packets from the XBee, I check the incoming source address and skip anything that isn't coming from the switch. I have a number of the Iris devices and they were messing up the experiment, so I just ignore them in the code. You'll have to change that to your own devices address.
#! /usr/bin/python # Have fun from xbee import ZigBee import logging import datetime import time import serial import sys, traceback import shlex from struct import * ''' Before we get started there's a piece of this that drove me nuts. Each message to a Zigbee cluster has a length and a header. The length isn't talked about at all in the Zigbee documentation (that I could find) and the header byte is drawn backwards to everything I've ever dealt with. So, I redrew the header byte so I could understand and use it: 7 6 5 4 3 2 1 0 X Disable Default Response 1 = return default message X Direction 1 = server to client, 0 = client to server X Manufacturer Specific X Frame Type 1 = cluster specific, 0 = entire profile So, to send a cluster command, set bit zero, and to get an attribute from a cluster set bit 4. If you want to be sure you get a reply, set the default response. I haven't needed the manufacturer specific bit yet. ''' def printData(data): print "********** Message Contents" for key, value in data.iteritems(): if key == "id": print key, value else: print key, "".join("%02x " % ord(b) for b in data[key]) print def getAttributes(data, thisOne): ''' OK, now that I've listed the clusters, I'm going to see about getting the attributes for one of them by sending a Discover attributes command. This is not a ZDO command, it's a ZCL command. ZDO = ZigBee device object - the actual device ZCL = Zigbee device cluster - the collection of routines to control it. Frame control field, bit field, first byte bits 0-1, frame type field 00 entire profile 01 specific to a particular cluster 10-11 reserved (don't use) Note, if you're sending commands, this should be 01 if you're reading attributes, it should be 00 bit 2, manufacturer specific, if this bit is set, include the manufacturer code (below) bit 3 direction, this determines if it is from a client to server. 1 server to client 0 client to server Note, server and client are specific to zigbee, not the purpose of the machine, so think about this. For example to turn an on/off switch on, you have to be the server so this bit will be 01 bit 4 disable default response bits 5-7 reserved (set to zero) Manufacturer code, either 2 bytes or not there Transaction sequence number, byte Command identifier, byte Frame payload, variable, the command bytes to do something frame control bits = 0b00 (this means a BINARY 00) manufacturer specific bit = 0, for normal, or one for manufacturer So, the frame control will be 0000000 discover attributes command identifier = 0x0c then a zero to indicate the first attribute to be returned and a 0x0f to indicate the maximum number of attributes to return. ''' print "Sending Discover Attributes" zb.send('tx_explicit', dest_addr_long = data['source_addr_long'], dest_addr = data['source_addr'], src_endpoint = '\x00', dest_endpoint = '\x01', cluster = thisOne, # cluster I want to know about profile = '\x01\x04', # home automation profile # means: frame control 0, sequence number 0xaa, command 0c, # start at 0x0000 for a length of 0x0f data = '\x00' + '\xaa' + '\x0c'+ '\x00' + '\x00'+ '\x0f' ) # this is a call back function. When a message # comes in this function will get the data def messageReceived(data): try: # This is the long address of my door switch device # since I have several other devices and they are transmitting # all the time, I'm excluding them and only allowing the # door switch in if data['source_addr_long'] != '\x00\x0d\x6f\x00\x03\xc2\x71\xcc': return print 'gotta packet', #print data if (data['id'] == 'rx_explicit'): print "RF Explicit" #printData(data) clusterId = (ord(data['cluster'][0])*256) + ord(data['cluster'][1]) print 'Cluster ID:', hex(clusterId), print "profile id:", repr(data['profile']) if (data['profile']=='\x01\x04'): # Home Automation Profile # This has to be handled differently than the general profile # each response if from a cluster that is actually doing something # so there are attributes and commands to think about. # # Since this is a ZCL message; which actually means this message is # is supposed to use the ZigBee cluster library to actually do something # like turn on a light or check to see if it's on, the command way down # in the rf_data is important. So, the commands may be repeated in # each cluster and do slightly different things # # I'm going to grab the cluster command out of the rf_data first so # I don't have to code it into each cluster #print "take this apart" #print repr(data['rf_data']) if (data['rf_data'][0] == '\x08'): # was it successful? #should have a bit check to see if manufacturer data is here cCommand = data['rf_data'][2] print "Cluster command: ", hex(ord(cCommand)) else: print "Cluster command failed" return # grab the payload data to make it easier to work with payload = data['rf_data'][3:] #from index 3 on is the payload for the command datatypes={'\x00':'no data', '\x10':'boolean', '\x18':'8 bit bitmap', '\x20':'unsigned 8 bit integer', '\x21':'unsigned 24 bit integer', '\x30':'8 bit enumeration', '\x42':'character string'} print "Raw payload:",repr(payload) # handle these first commands in a general way if (cCommand == '\x0d'): # Discover Attributes # This tells you all the attributes for a particular cluster # and their datatypes print "Discover attributes response" if (payload[0] == '\x01'): print "All attributes returned" else: print "Didn't get all the attributes on one try" i = 1 if (len(payload) == 1): # no actual attributes returned print "No attributes" return while (i < len(payload)-1): print " Attribute = ", hex(ord(payload[i+1])) , hex(ord(payload[i])), try: print datatypes[payload[i+2]] i += 3 except: print "I don't have an entry for datatype:", hex(ord(payload[i+2])) return if (clusterId == 0x0000): # Under HA this is the 'Basic' Cluster pass elif (clusterId == 0x0003): # 'identify' should make it flash a light or something pass elif (clusterId == 0x0004): # 'Groups' pass elif (clusterId == 0x0005): # 'Scenes' pass elif (clusterId == 0x0006): # 'On/Off' this is for switching or checking on and off print "inside cluster 6" elif (clusterId == 0x0008): # 'Level' pass else: print("Haven't implemented this yet") elif (data['profile']=='\x00\x00'): # The General Profile if (clusterId == 0x0000): print ("Network (16-bit) Address Request") #printData(data) elif (clusterId == 0x0004): # Simple Descriptor Request, print("Simple Descriptor Request") #printData(data) elif (clusterId == 0x0008): # I couldn't find a definition for this print("This was probably sent to the wrong profile") elif (clusterId == 0x0013): # This is the device announce message. print 'Device Announce Message' printData(data) print "sending Active Endpoint Request " zb.send('tx_explicit', dest_addr_long = data['source_addr_long'], dest_addr = data['source_addr'], src_endpoint = '\x00', dest_endpoint = '\x00', cluster = '\x05\x00', profile = '\xc2\x16', # The first item is a number to identify the message # The next two are the short address of the device data = '\x12' + data['source_addr'][1]+ data['source_addr'][0] ) elif (clusterId == 0x8000): print("Network (16-bit) Address Response") #printData(data) elif (clusterId == 0x8038): print("Management Network Update Request"); elif (clusterId == 0x8005): # this is the Active Endpoint Response This message tells you # what the device can do print 'Active Endpoint Response' printData(data) if (ord(data['rf_data'][1]) == 0): # this means success print "Active Endpoint reported back is: {0:02x}".format(ord(data['rf_data'][5])) print("Now trying simple descriptor request on an endpoint") zb.send('tx_explicit', dest_addr_long = data['source_addr_long'], dest_addr = data['source_addr'], src_endpoint = '\x00', dest_endpoint = '\x00', # This has to go to endpoint 0 ! cluster = '\x05\x00', #simple descriptor request' profile = '\xc2\x16', data = '\x13' + data['source_addr'][1] + data['source_addr'][0] + '\x01' ) elif (clusterId == 0x8004): print "simple descriptor response" try: clustersFound = [] r = data['rf_data'] if (ord(r[1]) == 0): # means success #take apart the simple descriptor returned endpoint, profileId, deviceId, version, inCount = \ unpack('<BHHBB',r[5:12]) print " endpoint reported is: {0:02x}".format(endpoint) print " profile id: {0:04x}".format(profileId) print " device id: {0:04x}".format(deviceId) print " device version: {0:02x}".format(version) print " input cluster count: {0:02x}".format(inCount) position = 12 # input cluster list (16 bit words) for x in range (0,inCount): thisOne, = unpack("<H",r[position : position+2]) clustersFound.append(r[position+1] + r[position]) position += 2 print " input cluster {0:04x}".format(thisOne) outCount, = unpack("<B",r[position]) position += 1 print " output cluster count: {0:02x}".format(outCount) #output cluster list (16 bit words) for x in range (0,outCount): thisOne, = unpack("<H",r[position : position+2]) clustersFound.append(r[position+1] + r[position]) position += 2 print " output cluster {0:04x}".format(thisOne) clustersFound.append('\x0b\x04') print "added special cluster" print "Completed Cluster List" except: print "error parsing Simple Descriptor" printData(data) print repr(clustersFound) for c in clustersFound: getAttributes(data, c) # Now, go get the attribute list for the cluster elif (clusterId == 0x0006): print "Match Descriptor Request" # Match Descriptor Request printData(data) # Now the Match Descriptor Response print "Sending match descriptor response" zb.send('tx_explicit', dest_addr_long = data['source_addr_long'], dest_addr = data['source_addr'], src_endpoint = '\x00', dest_endpoint = '\x00', cluster = '\x80\x06', profile = '\x00\x00', #seq #, status, address,num endp, list data = '\x00\x00\x00\x00\x01\x02') else: print ("Unimplemented Cluster ID", hex(clusterId)) print elif (data['profile']=='\xc2\x16'): # Alertme Specific printData(data) '''print "Sending weird messages" if (clusterId == 0x00f6): payload3 = '\x11\x01\x01' zb.send('tx_explicit', dest_addr_long = data['source_addr_long'], dest_addr = data['source_addr'], src_endpoint = '\x00', dest_endpoint = data['source_endpoint'], cluster = data['cluster'], profile = '\xc2\x16', data = payload3 ) payload4 = '\x19\x01\xfa\x00\x01' zb.send('tx_explicit', dest_addr_long = data['source_addr_long'], dest_addr = data['source_addr'], src_endpoint = '\x00', dest_endpoint = data['source_endpoint'], cluster = data['cluster'], profile = '\xc2\x16', data = payload4 )''' if (clusterId == 0x00ef): pass elif (clusterId == 0x00f0): pass else: print ("Unimplemented Profile ID") elif(data['id'] == 'route_record_indicator'): print("Route Record Indicator") else: print("some other type of packet") print(data) except: print "I didn't expect this error:", sys.exc_info()[0] traceback.print_exc() def sendSwitch(whereLong, whereShort, srcEndpoint, destEndpoint, clusterId, profileId, clusterCmd, databytes): payload = '\x11\x00' + clusterCmd + databytes # print 'payload', # for c in payload: # print hex(ord(c)), # print # print 'long address:', # for c in whereLong: # print hex(ord(c)), # print zb.send('tx_explicit', dest_addr_long = whereLong, dest_addr = whereShort, src_endpoint = srcEndpoint, dest_endpoint = destEndpoint, cluster = clusterId, profile = profileId, data = payload ) #------------ XBee Stuff ------------------------- # this is the /dev/serial/by-id device for the USB card that holds the XBee ZIGBEEPORT = "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A901QL3F-if00-port0" ZIGBEEBAUD_RATE = 9600 # Open serial port for use by the XBee ser = serial.Serial(ZIGBEEPORT, ZIGBEEBAUD_RATE) # The XBee addresses I'm dealing with BROADCAST = '\x00\x00\x00\x00\x00\x00\xff\xff' theSwitch = '\x00\x0d\x6f\x00\x03\x58\x05\xc2' UNKNOWN = '\xff\xfe' # This is the 'I don't know' 16 bit address #------------------------------------------------- logging.basicConfig() # Create XBee library API object, which spawns a new thread zb = ZigBee(ser, callback=messageReceived) print ("started") notYet = True; firstTime = True; while True: try: time.sleep(5) if (firstTime): # sendSwitch(whereLong=theSwitch, whereShort=UNKNOWN, srcEndpoint='\x00', # destEndpoint='\x00', clusterId='\x00\x00', profileId='\x00\x00', # clusterCmd='\x00', databytes='\x00') # print "sending Active Endpoint Request " # zb.send('tx_explicit', # dest_addr_long = theSwitch, # dest_addr = '\x94\x65', # src_endpoint = '\x00', # dest_endpoint = '\x00', # cluster = '\x00\x05', # profile = '\x00\x00', # # The first item is a number to identify the message # # The next two are the short address of the device # data = '\x12' + '\x65' + '\x94' # ) # time.sleep(10) # print "sending 'configure reporting'" # zb.send('tx_explicit', # dest_addr_long = theSwitch, # dest_addr = '\x94\x65', # src_endpoint = '\x00', # dest_endpoint = '\x01', # cluster = '\x00\x06', # cluster I want to deal with # profile = '\x01\x04', # home automation profile # data = '\x00' + '\xaa' + '\x06' + '\x00' + '\x00' + '\x00' + '\x10' + '\x00' + '\x00' + '\x00' + '\x40' + '\x00' + '\x00' # ) firstTime = False print ("tick") sys.stdout.flush() # if you're running non interactive, do this except KeyboardInterrupt: print ("Keyboard interrupt") break except: print ("I didn't expect this error:", sys.exc_info()[0]) traceback.print_exc() break print ("After the while loop") # halt() must be called before closing the serial # port in order to ensure proper thread shutdown zb.halt() ser.close()
I'm continuing to play with this switch as time, and new ideas permit, so I think the door switch can be conquered. However, if is as flaky as the initial results would indicate, I may not be using it. Having to have the magnet right up against the sensor and having so little elbow room would stop the switch from being useful on things like garage doors, doors that move due to weather or settling, wooden windows that may not shut all the way, that kind of thing. If someone has one that works well in an Iris setup can tell us about this switch, it would be great.
If you want, grab the code, experiment, play, and let me know the results you get.
The next post on this project is here <link>.
Didn't you get a Razberry a while back? I've never looked back myself, just look for better ways to do things. Have made several program changes as I've learned new and better ways to do things.
ReplyDeleteI did get a razberry!! It's installed on a Pi and doing absolutely nothing. My plan is to get into that bad boy this summer and control a couple of switches, then expand it as it goes.
DeleteBut, for some reason it just galls me that companies like Alertme produce this stuff and claim compatibility when they clearly don't have it. The point may be moot soon though, they got bought out and it's unclear what's going to happen going forward. The evil little boy in me comes out each time I see one of their devices.
Companies like Centralite make compliant Zigbee devices and controllers, but just haven't been able to penetrate the market.
Z-wave is definitely the way of the future, and the company that makes this is a good one.
I can add that, once you fiddle with this switch enough to get it working, you have a fully functional switch that works fine until you stop giving it closures to report for about 8 seconds, then it leaves the network again.
ReplyDeleteThe pushbutton on the board may be used for forcing the device to forget a network and start over, but it's also definitely a tamper switch, and there is a bit in the attribute for this point that will set/clear when you start to pull the case apart (when it's working).
I would love to be able to hijack this device, since I have about 8 of them on the IrisSmartHome network, but if it doesn't work out I'll have to chalk them up and look for another xbee-based contact sensor solution, so I can ditch Iris.
It looks like Zigbee is taking a back seat to Z-Wave. Although Z-Wave is a closed protocol and very expensive to get into, there are ways of using it without breaking the protocol.
DeleteThe really annoying thing about Zigbee is that the consortium didn't enforce their rules about meeting the spec and the manufacturers went their own way.
I haven't given up on this switch, but it is taking some time.
I think some Zigbee implementers took advantage of 'manufacturer specific' fields in the protocol headers that allow for customizations that make back-engineering really difficult.
DeleteI didn't think much about getting any z-wave products but that was back when I was buying canned systems and they were expensive. Now it looks like I should be picking up one of these Razberry units...
I replaced my X10 devices with Z-Wave quite a few years ago and never looked back. At first Misterhouse was the controller, but eventually I went with a Vera Lite on UI5, their older OS.
ReplyDeleteThe trouble is, Z-Wave doesn't do everything I want. So XBees fill in the gap. Stealing ideas from Dave, OpenSprinker and some of my own, I put together a Raspberry Pi as an XBee network gateway controller. Commands go back and forth between the Pi and the Vera.
An example of how it works is, I have a strip of LED lights along my stairway. It is controlled by an Arduino with XBee and a motion sensor. If I approach it from the bottom of the stairs, the Arduino senses the motion and turns on the LED lights. If I approach the stairs from the upper lever, a Z-Wave motion sensor is triggered, the Vera Lite sends a command to the Raspberry Pi, which in turn send the command to the Arduino controller, which turns on the lights. When no more motion is detected, the Arduino code times out and the lights go out.
That is totally AWESOME !
DeleteThis describes my house. I have a growing number of Arduino devices that communicate with the RPi using XBee. The RPi has control over them, but they will operate in a default mode without input from the main controller. The Z-Wave stuff comes in and it's one fun event after another. I have a door sensor that refuses to cooperate so it's getting retired in favor of a Fibaro unit. They seem to be a much better door sensor...
DeleteHave you made any progress on this? the Iris devices are nice and cheap, but I hate the closed and unreliable Iris hub and service.
ReplyDeleteYep, I have a couple of the contact switches running and occasionally use the key fob. My partner in doing this has a full system running and actually turned off his paid portion of the Iris service. He let the free portion alone since it's ... well free.
DeleteIt's not perfect though. Since he only has one router, if he moves it, he has to set the network up again. I have more routers than switches, so it doesn't affect me that way.
All you have to do is follow the trail of postings on it. I have enough code on line to get you started in bringing up your own system any way you would like to do it. At some point in the distant future (months), I plan on revisiting this and trying to get the dependence on a router out of the picture, but that's going to be a lot of head scratching, and I've got summer stuff to do.
Great post
ReplyDeletereplying to MIchael Yeager : "I should be using LinkedIn more often than I am.. my direct email is dennispford75@gmail.com. " i had Iris until i just gave up on there crappy support, these Gen 1 devices worked perfectly on doors in all environments (I live in Chicago so -15 to 115 degF). I have 4 of them and don't want to spend $100+ on more devices, if you programmed an IDE for this can you provide the location onf GitHub, i would like to try it with SmartThings. Dennis
ReplyDeleteI would like that too, but seems like he stopped posting and now Iris is defunct
DeleteI haven't stopped, just slowed way down to do some stuff that isn't technical while I actually enjoy the things I've already done.
DeleteThere are some cool devices now that I may start to look at though. The stupid Wemo devices can't be trusted and some of the others are way to cloud oriented for my taste.
Anyway, all my stuff is on github and has been for quite some time.
So, Dave, thought I would catch up after four years. Last time we talked you turned me on to MQTT and life has never been the same. Pub/sub is definitely the way to do home automation!
ReplyDeleteAnyway, as a point of interest, I just retired all the Iris door sensors, and the entire zigbee kingdom altogether. Got tired of the flaky join/quit/rejoin, pain-of-adding-a-new-sensor, expensive batteries, etc etc etc. I started over with Honeywell 5800mini door/window sensors that run in the 350-400 Mhz band, and an 'RTL-SDR BLOG' USB software-defined radio. Long story short, in TWO DAYS I revamped the system and it works like a charm; bonus is it's DIRT EASY, as it's JSON and MQTT friendly. Beats the MONTHS of time I spent working on hacking zigbee/Iris with Python.
https://www.amazon.com/gp/product/B01MEGAWKR/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1
https://www.amazon.com/gp/product/B011HVUEME/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1
cheers
OK, I'm interested. Actually, I'm very interested. I'll be looking into that.
Delete