[OBSOLETE] Iris Motion Sensor (2nd gen, 3326-L)

If you would stayed with ST’s device handler after you changed to it, and then re-paired the device, it should have started working as expected. It’s the re-pairing process that forces it to work.

That is good news, I can get the next motion sensor to run local and then two things for the alarm. A second Aeon Siren and another leak detector. I like the Everspring as it is mounted up high and if the probe gets wet who cares, but the ST version is not waterproof, so it seems to be a one time use then throw it out. So my game plan is to by an ST unit then attach leads to the two bottom probes and a leak sensor at the end available on Amazon for a few bucks. I will see if I can crimp it on if not I will solder it on so it will be above water damage level. Then at least the water detector will run local. Items such as leak detectors should run local as you can’t depend on the internet. If I could figure out how to install a cellular modem to my setup I would do so to bypass the problem of sending sms alerts as they use the web.

1 Like

If you’re going to go that route, couldn’t you use a contact sensor that 1) has a dry contact connector, and 2) runs locally? Then you could hook the leak sensor to the dry contact connections. Seems a little cleaner than messing with the ST leak sensor, maybe.

@chickwebb,

I did what you stated using Ecolink sensors for many things, but I had to develop my own device handlers to add capabilities to that so they’d be recognized as the device I wanted them to be within Routines and other SmartApps looking for specific capabilities. In my cases, door lock monitors just to name one. Since I used my own DH, it no longer runs locally. @joelw135’s use case is to run locally as much as possible, if not everything. Using an ST sensor and standard DH in his use case, it would run local.

Here is what I am talking about: Water sensor

Now with the SHM or SA there is a separate section for leak detectors so that is my goal to determine what is leaking rather than a general alarm. As it is now when water is detected it sets off the Siren, I would prefer a different sound for the water over intrusion alarm.

@mitchp, can this device be modified to report temperature with a 0.1F change? I am using it for temperature control and it is not reporting frequently enough.

Yes, it can. I modified the choice for the iris contact sensor to report five places right of the decimal.

@bamarayne, can you tell me what I have to modify, or share the modified code?

Ok, no problem. I just went and looked at it. I’m not using @mitchp code. I think it was the stock st code I modified.

Either way, I’ll post I in a minute. I have to get on my computer.

1 Like

Ugh… I’m using the code for the open/close sensors, not the motion sensors…

Let me figure out what I changed and it’s probably the same in the motion sensor code.

I have the IRIS Open/Close sensors in some of the bedrooms too, so that code will also help.

I would love to do this with the temp, but I also want all my alram stuff to run local. I am even going to figure out how to dial out on cellular if I loose Internet.

1 Like

@bamarayne, I am using the IRIS Open/Close sensors in the bedrooms, so the modified device code for reporting more frequently will be very helpful to me.

Oh man! I am so sorry. I started trying to figure out what I did to change the code last night. I was trying to remember and then modify the motion sensor code. I totally forgot to post it.

I will in just a few minutes.

@damohabir

This is the code I am using. I don’t remember where I got it. I did not write it. I just modified it and now I don’t remember what I did. LOL

/**

  • Iris Open/Closed Sensor for temp
  • Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except
  • in compliance with the License. You may obtain a copy of the License at:
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  • on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  • for the specific language governing permissions and limitations under the License.

/
metadata {
definition (name: “Iris Open/Closed Sensor for Temp”, namespace: “mgspivey”, author: “Mark Spivey/SmartThings”) {
capability “Battery”
capability “Configuration”
capability “Contact Sensor”
capability “Refresh”
capability “Temperature Measurement”
command “enrollResponse”
fingerprint endpointId: “01”, inClusters: “0000,0001,0003,0402,0500,0020,0B05”, outClusters: “0019”, model: “3320-L”, manufacturer: “CentraLite”
}
simulator {
}
preferences {
input description: “This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that’s 5 degrees too warm, you’d enter "-5". If 3 degrees too cold, enter "+3".”, displayDuringSetup: false, type: “paragraph”, element: “paragraph”
input “tempOffset”, “number.number”, title: “Temperature Offset”, description: “Adjust temperature by this many degrees”, range: "
…*“, displayDuringSetup: false
}
tiles(scale: 2) {
multiAttributeTile(name:“contact”, type: “generic”, width: 6, height: 4){
tileAttribute (“device.contact”, key: “PRIMARY_CONTROL”) {
attributeState “open”, label:‘${name}’, icon:“st.contact.contact.open”, backgroundColor:”#ffa81e"
attributeState “closed”, label:‘${name}’, icon:“st.contact.contact.closed”, backgroundColor:“#79b821
}
}
valueTile(“temperature”, “device.temperature”, inactiveLabel: false, width: 2, height: 2) {
state “temperature”, label:‘${currentValue}°’,
backgroundColors:[
[value: 31, color: “#153591”],
[value: 44, color: “#1e9cbb”],
[value: 59, color: “#90d2a7”],
[value: 74, color: “#44b621”],
[value: 84, color: “#f1d801”],
[value: 95, color: “#d04e00”],
[value: 96, color: “#bc2323”]
]
}
valueTile(“battery”, “device.battery”, decoration: “flat”, inactiveLabel: false, width: 2, height: 2) {
state “battery”, label:‘${currentValue}% battery’, unit:“”
}
standardTile(“refresh”, “device.refresh”, inactiveLabel: false, decoration: “flat”, width: 2, height: 2) {
state “default”, action:“refresh.refresh”, icon:“st.secondary.refresh”
}
main ([“contact”, “temperature”])
details([“contact”,“temperature”,“battery”,“refresh”])
}
}
def parse(String description) {
log.debug “description: $description”
Map map = [:]
if (description?.startsWith(‘catchall:’)) {
map = parseCatchAllMessage(description)
}
else if (description?.startsWith(‘read attr -’)) {
map = parseReportAttributeMessage(description)
}
else if (description?.startsWith('temperature: ')) {
map = parseCustomMessage(description)
}
else if (description?.startsWith(‘zone status’)) {
map = parseIasMessage(description)
}
log.debug “Parse returned $map”
def result = map ? createEvent(map) : null
if (description?.startsWith(‘enroll request’)) {
List cmds = enrollResponse()
log.debug “enroll response: ${cmds}”
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
}
return result
}
private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def cluster = zigbee.parse(description)
if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) {
case 0x0001:
resultMap = getBatteryResult(cluster.data.last())
break
case 0x0402:
log.debug ‘TEMP’
// temp is last 2 data values. reverse to swap endian
String temp = cluster.data[-2…-1].reverse().collect { cluster.hex1(it) }.join()
def value = getTemperature(temp)
resultMap = getTemperatureResult(value)
break
}
}
return resultMap
}
private boolean shouldProcessMessage(cluster) {
// 0x0B is default response indicating message got through
// 0x07 is bind message
boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B ||
cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage
}
private Map parseReportAttributeMessage(String description) {
Map descMap = (description - “read attr - “).split(”,”).inject([:]) { map, param →
def nameAndValue = param.split(“:”)
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
log.debug “Desc Map: $descMap”
Map resultMap = [:]
if (descMap.cluster == “0402” && descMap.attrId == “0000”) {
def value = getTemperature(descMap.value)
resultMap = getTemperatureResult(value)
}
else if (descMap.cluster == “0001” && descMap.attrId == “0020”) {
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
}
return resultMap
}
private Map parseCustomMessage(String description) {
Map resultMap = [:]
if (description?.startsWith('temperature: ‘)) {
def value = Double.parseDouble(description.split(": ")[1])
resultMap = makeTemperatureResult(convertTemperature(value))
}
return resultMap
}
private Map parseIasMessage(String description) {
List parsedMsg = description.split(’ ')
String msgCode = parsedMsg[2]
Map resultMap = [:]
switch(msgCode) {
case ‘0x0020’: // Closed/No Motion/Dry
resultMap = getContactResult(‘closed’)
log.info “Contact is Closed”
break
case ‘0x0021’: // Open/Motion/Wet
resultMap = getContactResult(‘open’)
log.info “Contact is Open”
break
case ‘0x0022’: // Tamper Alarm
log.info “Tamper Alarm”
break
case ‘0x0023’: // Battery Alarm
log.info “Battery Alarm”
break
case ‘0x0024’: // Supervision Report
resultMap = getContactResult(‘closed’)
log.info “Contact is Closed”
break
case ‘0x0025’: // Restore Report
resultMap = getContactResult(‘open’)
log.info “Contact is Open”
break
case ‘0x0026’: // Trouble/Failure
log.info “Trouble Alarm”
break
case ‘0x0028’: // Test Mode
break
}
return resultMap
}
def getTemperature(value) {
def celsius = Integer.parseInt(value, 16).shortValue() / 100
if(getTemperatureScale() == “C”){
log.info “Temperature is ${celsius}C with an offset of ${tempOffset}C”
return celsius
} else {
def fahrenheit = celsiusToFahrenheit(celsius)
log.info “Temperature is ${fahrenheit}F with an Offset of ${tempOffset}F”
return fahrenheit
}
}
private def convertTemperature(celsius) {
if(getTemperatureScale() == “C”){
return celsius
} else {
def fahrenheit = Math.round(celsiusToFahrenheit(celsius) * 100) /100
return fahrenheit
}
}
private Map getBatteryResult(rawValue) {
log.debug ‘Battery’
def linkText = getLinkText(device)
def result = [
name: ‘battery’,
value: ‘–’
]
def volts = rawValue / 10
def descriptionText
if (rawValue == 0) {}
else {
if (volts > 3.5) {
result.descriptionText = “${linkText} battery has too much power (${volts} volts).”
}
else if (volts > 0){
def minVolts = 2.1
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100)
result.descriptionText = “${linkText} battery was ${result.value}%”
}
}
log.info “Battery is ${result.value}%”
log.info “Battery voltage is ${volts}v”
return result
}
private Map getTemperatureResult(value) {
log.debug ‘TEMP’
def linkText = getLinkText(device)
if (tempOffset) {
def offset = tempOffset.toBigDecimal()
def v = value
value = value + tempOffset.toBigDecimal()
}
def descriptionText = “${linkText} was ${value}°${temperatureScale}”
log.info “${linkText} reports as ${value}°${temperatureScale}”
return [
name: ‘temperature’,
value: value,
descriptionText: descriptionText
]
}
private Map makeTemperatureResult(value) {
def linkText = getLinkText(device)
if (tempOffset) {
def offset = tempOffset.toBigDecimal()
def v = value
value = value + tempOffset.toBigDecimal()
}
def descriptionText = “${linkText} was ${value}°${temperatureScale}”
log.info “${linkText} reports as ${value}°${temperatureScale}”
return [
name: ‘temperature’,
value: value,
descriptionText: descriptionText
]
}
private Map getContactResult(value) {
log.debug ‘Contact Status’
def linkText = getLinkText(device)
def descriptionText = “${linkText} was ${value == ‘open’ ? ‘opened’ : ‘closed’}”
return [
name: ‘contact’,
value: value,
descriptionText: descriptionText
]
}
def refresh() {
log.debug “Manual Refresh Initiated”
def refreshCmds = [
“st rattr 0x${device.deviceNetworkId} 1 0x402 0”, “delay 200”,
“st rattr 0x${device.deviceNetworkId} 1 1 0x20”, “delay 200”
]
return refreshCmds + enrollResponse()
}
def configure() {
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug “Configuring Reporting, IAS CIE, and Bindings.”
def configCmds = [
“zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}”, “delay 200”,
“send 0x${device.deviceNetworkId} 1 1”, “delay 500”,
“zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}”, “delay 500”,
“zcl global send-me-a-report 1 0x20 0x20 30 1200 {01}”, //checkin time 20 minutes
“send 0x${device.deviceNetworkId} 1 1”, “delay 500”,
“zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}”, “delay 500”,
“zcl global send-me-a-report 0x402 0 0x29 30 1200 {0A00}”,
“send 0x${device.deviceNetworkId} 1 1”, “delay 500”
]
return configCmds + refresh() // send refresh cmds as part of config
}
def enrollResponse() {
log.debug “Sending enroll response”
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
[
//Resending the CIE in case the enroll request is sent before CIE is written
“zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}”, “delay 200”,
“send 0x${device.deviceNetworkId} 1 ${endpointId}”, “delay 500”,
//Enroll Response
“raw 0x500 {01 23 00 00 00}”,
“send 0x${device.deviceNetworkId} 1 1”, “delay 200”
]
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value) {
new BigInteger(Math.round(value).toString()).toString(16)
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private byte reverseArray(byte array) {
int i = 0;
int j = array.length - 1;
byte tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j–;
i++;
}
return array
}

That code will set the sensor to report every .1 C change. If you need F, the reporting should be set to report on a change of 0x0005 (.056 C ≈ .1 F)

This will likely drain the batteries pretty quickly. Plus, the sensor in these might not be that precise, so take that into consideration.

I was measuring against a digital thermometer and they started pretty accurate over time. I could use the off-set and get them to reliably report within a range if 1/2 to 1/10 of a degree.

I didn’t notice too much on the battery, so I can’t say for sure there.

1 Like

It’s dead again after the new battery and 11 days. I tried the reset button. Seems to be sucking the battery dry at a very rapid rate. Must be a bad unit. Grrrr.

I bought two of these, and they both reported temperature every hour.
Loved them, so I bought two more.
Both of these don’t report the temperature regularly.

Any idea what could be up or how to fix it?

I purchased 2 of the iris motion sensor. They work very well, the only problem that I am seeing is that the battery is not lasting. I only have had them for 2 weeks and they both are showing 77% battery. Do you think it’s the battery that comes with it or maybe it’s being reported wrong. If anyone has a solution please chime in.

Thanks