Last active
May 15, 2016 19:06
-
-
Save mckeed/230252b26f64f8853a73 to your computer and use it in GitHub Desktop.
Aeon Labs Multisensor 6 device handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2015 SmartThings | |
* | |
* 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: "Aeon Multisensor 6", namespace: "smartthings", author: "SmartThings") { | |
capability "Motion Sensor" | |
capability "Temperature Measurement" | |
capability "Relative Humidity Measurement" | |
capability "Illuminance Measurement" | |
capability "Ultraviolet Index" | |
capability "Configuration" | |
capability "Sensor" | |
capability "Battery" | |
attribute "tamper", "enum", ["detected", "clear"] | |
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A" | |
} | |
simulator { | |
status "no motion" : "command: 9881, payload: 00300300" | |
status "motion" : "command: 9881, payload: 003003FF" | |
for (int i = 0; i <= 100; i += 20) { | |
status "temperature ${i}F": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate( | |
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport( | |
scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1) | |
).incomingMessage() | |
} | |
for (int i = 0; i <= 100; i += 20) { | |
status "humidity ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate( | |
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: i, sensorType: 5) | |
).incomingMessage() | |
} | |
for (int i in [0, 20, 89, 100, 200, 500, 1000]) { | |
status "illuminance ${i} lux": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate( | |
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(scaledSensorValue: i, sensorType: 3) | |
).incomingMessage() | |
} | |
for (int i in [0, 5, 10, 15, 50, 99, 100]) { | |
status "battery ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate( | |
new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i) | |
).incomingMessage() | |
} | |
status "low battery alert": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate( | |
new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 255) | |
).incomingMessage() | |
status "wake up" : "command: 8407, payload: " | |
} | |
tiles { | |
standardTile("motion", "device.motion", width: 2, height: 2) { | |
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0" | |
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff" | |
} | |
valueTile("temperature", "device.temperature", inactiveLabel: false) { | |
state "temperature", label:'${currentValue}°', | |
backgroundColors:[ | |
[value: 32, color: "#153591"], | |
[value: 44, color: "#1e9cbb"], | |
[value: 59, color: "#90d2a7"], | |
[value: 74, color: "#44b621"], | |
[value: 84, color: "#f1d801"], | |
[value: 92, color: "#d04e00"], | |
[value: 98, color: "#bc2323"] | |
] | |
} | |
valueTile("humidity", "device.humidity", inactiveLabel: false) { | |
state "humidity", label:'${currentValue}% humidity', unit:"" | |
} | |
valueTile("illuminance", "device.illuminance", inactiveLabel: false) { | |
state "luminosity", label:'${currentValue} ${unit}', unit:"lux" | |
} | |
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") { | |
state "battery", label:'${currentValue}% battery', unit:"" | |
} | |
main(["motion", "temperature", "humidity", "illuminance"]) | |
details(["motion", "temperature", "humidity", "illuminance", "battery"]) | |
} | |
} | |
def updated() | |
{ | |
if (state.sec && !isConfigured()) { | |
// in case we miss the SCSR | |
response(configure()) | |
} | |
} | |
def parse(String description) | |
{ | |
def result = null | |
if (description.startsWith("Err 106")) { | |
result = createEvent( eventType: "ALERT", name: "secureInclusion", value: "failed", isStateChange: true, | |
descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.") | |
} else if (description != "updated") { | |
def cmd = zwave.parse(description, [0x31: 5, 0x30: 2, 0x84: 1]) | |
if (cmd) { | |
result = zwaveEvent(cmd) | |
} | |
} | |
log.debug "Parsed '${description}' to ${result.inspect()}" | |
return result | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) | |
{ | |
def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)] | |
if (!isConfigured()) { | |
// we're still in the process of configuring a newly joined device | |
log.debug("late configure") | |
result += response(configure()) | |
} else { | |
result += response(zwave.wakeUpV1.wakeUpNoMoreInformation()) | |
} | |
result | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { | |
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x30: 2, 0x84: 1]) | |
state.sec = 1 | |
log.debug "encapsulated: ${encapsulatedCommand}" | |
if (encapsulatedCommand) { | |
zwaveEvent(encapsulatedCommand) | |
} else { | |
log.warn "Unable to extract encapsulated cmd from $cmd" | |
createEvent(descriptionText: cmd.toString()) | |
} | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) { | |
response(configure()) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { | |
def map = [ name: "battery", unit: "%" ] | |
if (cmd.batteryLevel == 0xFF) { | |
map.value = 1 | |
map.descriptionText = "${device.displayName} battery is low" | |
map.isStateChange = true | |
} else { | |
map.value = cmd.batteryLevel | |
} | |
state.lastbatt = new Date().time | |
createEvent(map) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) | |
{ | |
def map = [:] | |
switch (cmd.sensorType) { | |
case 1: | |
map.name = "temperature" | |
def cmdScale = cmd.scale == 1 ? "F" : "C" | |
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) | |
map.unit = getTemperatureScale() | |
break | |
case 3: | |
map.name = "illuminance" | |
map.value = cmd.scaledSensorValue.toInteger() | |
map.unit = "lux" | |
break | |
case 5: | |
map.name = "humidity" | |
map.value = cmd.scaledSensorValue.toInteger() | |
map.unit = "%" | |
break | |
case 0x1B: | |
map.name = "ultravioletIndex" | |
map.value = cmd.scaledSensorValue.toInteger() | |
break | |
default: | |
map.descriptionText = cmd.toString() | |
} | |
createEvent(map) | |
} | |
def motionEvent(value) { | |
def map = [name: "motion"] | |
if (value) { | |
map.value = "active" | |
map.descriptionText = "$device.displayName detected motion" | |
} else { | |
map.value = "inactive" | |
map.descriptionText = "$device.displayName motion has stopped" | |
} | |
createEvent(map) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) { | |
setConfigured() | |
motionEvent(cmd.sensorValue) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { | |
motionEvent(cmd.value) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { | |
def result = [] | |
if (cmd.notificationType == 7) { | |
switch (cmd.event) { | |
case 0: | |
result << motionEvent(0) | |
result << createEvent(name: "tamper", value: "clear", displayed: false) | |
break | |
case 3: | |
result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName was moved") | |
break | |
case 7: | |
result << motionEvent(1) | |
break | |
} | |
} else { | |
result << createEvent(descriptionText: cmd.toString(), isStateChange: false) | |
} | |
result | |
} | |
def zwaveEvent(physicalgraph.zwave.Command cmd) { | |
createEvent(descriptionText: cmd.toString(), isStateChange: false) | |
} | |
def configure() { | |
// This sensor joins as a secure device if you double-click the button to include it | |
if (device.device.rawDescription =~ /98/ && !state.sec) { | |
log.debug "Multi 6 not sending configure until secure" | |
return [] | |
} | |
log.debug "Multi 6 configure()" | |
def request = [ | |
// send no-motion report 20 seconds after motion stops | |
zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: 20), | |
// report automatically on threshold change | |
zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 1), | |
zwave.batteryV1.batteryGet(), | |
zwave.sensorBinaryV2.sensorBinaryGet(sensorType: 0x0C), | |
] | |
commands(request) + ["delay 20000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()] | |
} | |
def setConfigured() { | |
updateDataValue("configured", "true") | |
} | |
def isConfigured() { | |
getDataValue("configured") == "true" | |
} | |
private command(physicalgraph.zwave.Command cmd) { | |
if (state.sec) { | |
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() | |
} else { | |
cmd.format() | |
} | |
} | |
private commands(commands, delay=200) { | |
delayBetween(commands.collect{ command(it) }, delay) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment