Last active
December 30, 2016 01:42
-
-
Save Njhaley/034f4cb86506cb97c2aa70fc793e6750 to your computer and use it in GitHub Desktop.
Zooz 4-in-1 ALT
This file contains 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
/* | |
* V 1.2 of zooZ 4-in-1 sensor code 6/12/2016 | |
* by Njhaley | |
* | |
* 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: "zooZ 4-in-1 sensor - njha. Alt", namespace: "njhaley", author: "njhaley") { | |
capability "Motion Sensor" | |
capability "Acceleration Sensor" | |
capability "Temperature Measurement" | |
capability "Relative Humidity Measurement" | |
capability "Illuminance Measurement" | |
capability "Configuration" | |
capability "Sensor" | |
capability "Battery" | |
capability "Thermostat" | |
// RAW Description: 0 0 0x0701 0 0 0 e 0x5E 0x98 0x86 0x72 0x5A 0x85 0x59 0x73 0x80 0x71 0x31 0x70 0x84 0x7A | |
attribute "tamper", "enum", ["detected", "clear"] | |
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x31,0x70,0x5A,0x98,0x7A" | |
} | |
/* simulator { | |
status "no motion" : "command: 9881, payload: 00300300" | |
status "motion" : "command: 9881, payload: 003003FF" | |
status "clear" : " command: 9881, payload: 0071050000000007030000" | |
status "tamper" : "command: 9881, payload: 007105000000FF07030000" | |
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: 2, | |
sensorType: 1, | |
scale: 1 | |
) | |
).incomingMessage() | |
} | |
for (int i = 0; i <= 100; i += 20) { | |
status "RH ${i}%": new physicalgraph.zwave.Zwave().securityV1.securityMessageEncapsulation().encapsulate( | |
new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport( | |
scaledSensorValue: i, | |
sensorType: 5 | |
) | |
).incomingMessage() | |
} | |
for (int i in [0, 1, 2, 8, 12, 16, 20, 24, 30, 64, 82, 100, 200, 500, 1000]) { | |
status "illuminance ${i}%": 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 (scale: 2) { | |
multiAttributeTile(name:"main", type:"generic", width:6, height:4) { | |
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") { | |
attributeState "temperature",label:'${currentValue}°', precision:2, backgroundColors:[ | |
[value: 25, color: "#153591"], | |
[value: 37, color: "#1e9cbb"], | |
[value: 50, color: "#90d2a7"], | |
[value: 62, color: "#44b621"], | |
[value: 75, color: "#f1d801"], | |
[value: 87, color: "#d04e00"], | |
[value: 100, color: "#bc2323"], | |
[value: 105, color: "#7F0707"] | |
] | |
} | |
} | |
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("humidity","device.humidity", width: 2, height: 2) { | |
state "humidity",label:'RH ${currentValue}%', precision:2 | |
} | |
} | |
valueTile("battery", "device.battery", decoration: "flat", width: 2, height: 2) { | |
state "battery", label:'${currentValue}% battery' | |
} | |
main(["main"]) | |
details(["main","humidity","motion","battery"]) | |
} | |
preferences { | |
input "debugOutput", "boolean", | |
title: "Enable debug logging?", | |
defaultValue: false, | |
displayDuringSetup: true | |
input "LEDbehavior", "enum", | |
title: "LED Behavior", | |
options: ["LED Off", "Breathing", "Quick Blink on Temp/PIR"], | |
defaultValue: "Quick Blink on Temp/PIR", | |
required: false, | |
displayDuringSetup: false | |
input "tempoffset", "number", | |
title: "Reporting threshold for temp", | |
description: "Enter a value 1-50 changing reporting threshold for temp. Represents 0.1 degree increments.", | |
range: "1..50", | |
defaultValue: 50, | |
required: false, | |
displayDuringSetup: false | |
input "humidityoffset", "number", | |
title: "Reporting threshold for humidity", | |
description: "Report when change occurs from 1%-50% RH)", | |
range: "1..50", | |
defaultValue: 1, | |
required: false, | |
displayDuringSetup: false | |
input "luminanceoffset", "number", | |
title: "Reporting threshold for Luminance", | |
description: "valid values from 5% to 50%", | |
range: "5..50", | |
defaultValue: 5, | |
required: false, | |
displayDuringSetup: false | |
input "PIRsensitivity", "number", | |
title: "PIR motion sensitivity", | |
description: "A value from 1-7, from high to low sensitivity (1 is highest)", | |
range: "1..7", | |
defaultValue: 4, | |
required: false, | |
displayDuringSetup: true | |
input "MotionReset", "number", | |
title: "PIR reset time", | |
description: "Number of minutes to wait to report motion cleared after a motion event if there is no motion detected.", | |
range: "1..255", | |
defaultValue: 5, | |
required: false, | |
displayDuringSetup: true | |
} | |
def updated() | |
{ | |
updateDataValue("configured", "false") | |
state.debug = ("true" == debugOutput) | |
} | |
def parse(String description) | |
{ | |
def result = null | |
if (description.startsWith("Err 106")) { | |
state.sec = 0 | |
result = createEvent( 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, 0x71:3, 0x7A: 2, 0x81: 1, 0x84: 2, 0x86: 2]) | |
if (cmd) { | |
result = zwaveEvent(cmd) | |
} | |
} | |
if (state.debug) log.debug "Parsed '${description}' to ${result.inspect()}" | |
return result | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.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 | |
if (state.debug) log.debug("late configure") | |
result += response(configure()) | |
} else { | |
result += response(checkBattery()) | |
} | |
result | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { | |
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x71: 3, 0x7A: 2, 0x81: 1, 0x84: 2]) | |
state.sec = 1 | |
// if (state.debug) 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) { | |
if (state.debug) log.debug "---SECURITY COMMANDS SUPPORTED REPORT V1--- ${device.displayName} sent commandClassControl: ${cmd.commandClassControl}, commandClassSupport: ${cmd.commandClassSupport}, reportsToFollow: ${cmd.reportsToFollow}" | |
response(configure()) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) { | |
if (state.debug) log.debug "---MANUFACTURER SPECIFIC REPORT V2--- ${device.displayName} sent deviceIdDataFormat: ${cmd.deviceIdDataFormat}, deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}, deviceIdType: ${cmd.deviceIdType}, payload: ${cmd.payload}" | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionCommandClassReport cmd) { | |
if (state.debug) log.debug "---COMMAND CLASS VERSION REPORT V1--- ${device.displayName} has command class version: ${cmd.commandClassVersion} - payload: ${cmd.payload}" | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { | |
def fw = "${cmd.applicationVersion}.${cmd.applicationSubVersion}" | |
updateDataValue("fw", fw) | |
if (state.debug) log.debug "---VERSION REPORT V1--- ${device.displayName} is running firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}" | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { | |
if (state.debug) log.debug "---CONFIGURATION REPORT V1--- ${device.displayName} parameter ${cmd.parameterNumber} with a byte size of ${cmd.size} is set to ${cmd.configurationValue}" | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { | |
if (state.debug) log.debug "---CONFIGURATION REPORT V2--- ${device.displayName} parameter ${cmd.parameterNumber} with a byte size of ${cmd.size} is set to ${cmd.configurationValue}" | |
} | |
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 | |
createEvent(map) | |
} else { | |
map.value = cmd.batteryLevel | |
createEvent(map) | |
} | |
state.lastbatt = now() | |
createEvent(map) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { | |
if (state.debug) log.debug "---SENSOR MULTILEVEL v5 REPORT--- ${device.displayName} sent sensorType: ${cmd.sensorType} value: ${cmd.sensorValue} scale: ${cmd.scale} scaledSensorValue: ${cmd.scaledSensorValue}" | |
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 | |
map.unit = "%" | |
break; | |
case 5: | |
map.name = "humidity" | |
map.value = cmd.scaledSensorValue | |
map.unit = "%" | |
break; | |
default: | |
map.descriptionText = cmd.toString() | |
} | |
createEvent(map) | |
} | |
def motionEvent(value) { | |
def map = [name: "motion"] | |
if (value != 0) { | |
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) { | |
if (state.debug) log.debug "---SENSOR BINARY REPORT V2--- ${device.displayName} sent value: ${cmd.sensorValue}" | |
motionEvent(cmd.sensorValue) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { | |
if (state.debug) log.debug "---BASIC SET REPORT V1--- ${device.displayName} sent value: ${cmd.value}" | |
motionEvent(cmd.value) | |
} | |
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { | |
if (state.debug) log.debug "---NOTIFICATION REPORT V3--- ${device.displayName} sent eventParametersLength: ${cmd.eventParametersLength} eventParameter: ${cmd.eventParameter} notificationType: ${cmd.notificationType} event: ${cmd.event}" | |
def result = [] | |
if (cmd.notificationType == 7) { | |
if (cmd.event == 0x00 && cmd.eventParameter == 0x08) { | |
result << motionEvent(0) | |
} | |
else if (cmd.event == 0x03) { | |
result << createEvent(name: "acceleration", value: "active", descriptionText: "$device.displayName cover is open.") | |
} | |
else if (cmd.event == 0X00 || cmd.eventParameter == 0x03) { | |
result << createEvent(name: "acceleration", value: "inactive", descriptionText: "$device.displayName cover has been closed.") | |
} | |
else if (cmd.event == 0x08) { | |
result << motionEvent(255) | |
} | |
} | |
else { | |
result << createEvent(descriptionText: cmd.toString(), isStateChange: false) | |
} | |
result | |
} | |
def zwaveEvent(physicalgraph.zwave.Command cmd) { | |
createEvent(descriptionText: cmd.toString(), isStateChange: false) | |
} | |
private checkBattery() { | |
def request = [ | |
zwave.batteryV1.batteryGet(), | |
zwave.wakeUpV1.wakeUpNoMoreInformation() | |
] | |
commands(request) | |
} | |
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) { | |
// if (state.debug) log.debug "4-in-1 sensor not sending configure until secure" | |
// return [] | |
//} | |
if (state.debug) log.debug "--Sending configuration commands to zooZ 4-in-1 sensor--" | |
//if (state.debug) log.debug "Prefernces settings: PIRsensitivity: $PIRsensitivity, Temp offset: $tempoffset, Humidity offset: $humidityoffset, Luminance offset: $luminanceoffset" | |
def LEDbehav = 3 | |
if (LEDbehavior == "LED Off") { | |
LEDbehav=1 | |
} | |
else if (LEDbehavior == "Breathing") { | |
LEDbehav=2 | |
} | |
else { | |
LEDbehav=3 | |
} | |
def PIRsens = 4 | |
if (PIRsensitivity) { | |
PIRsens=PIRsensitivity | |
} | |
else { | |
PIRsens = 4 | |
} | |
def MotionRst = 3 | |
if (MotionReset) { | |
MotionRst=MotionReset | |
} | |
else { | |
MotionRst = 3 | |
} | |
def tempoff = 1 | |
if (tempoffset) { | |
tempoff=tempoffset | |
} | |
else { | |
tempoff = 1 | |
} | |
def humidityoff = 10 | |
if (humidityoffset) { | |
humidityoff=humidityoffset | |
} | |
else { | |
humidityoff = 10 | |
} | |
def luminanceoff = 10 | |
if (luminanceoffset) { | |
luminanceoff=luminanceoffset | |
} | |
else { | |
luminanceoff = 10 | |
} | |
if (state.debug) log.debug "settings: ${settings.inspect()}, state: ${state.inspect()}" | |
setConfigured() | |
def request = [ | |
// set wakeup interval to 20 mins | |
zwave.wakeUpV1.wakeUpIntervalSet(seconds:3600, nodeid:zwaveHubNodeId), | |
// Get Version information | |
zwave.versionV1.versionGet(), | |
zwave.firmwareUpdateMdV2.firmwareMdGet(), | |
// configure temp scale to celcius or fahrenheight and set offset | |
zwave.configurationV1.configurationSet(parameterNumber: 0x01, size: 1, scaledConfigurationValue: 1), | |
zwave.configurationV1.configurationGet(parameterNumber: 0x01), | |
zwave.configurationV1.configurationSet(parameterNumber: 0x02, size: 1, scaledConfigurationValue: tempoff), | |
zwave.configurationV1.configurationGet(parameterNumber: 0x02), | |
// configure humidity offset | |
zwave.configurationV1.configurationSet(parameterNumber: 0x03, size: 1, scaledConfigurationValue: humidityoff), | |
zwave.configurationV1.configurationGet(parameterNumber: 0x03), | |
// configure luminance offset | |
zwave.configurationV1.configurationSet(parameterNumber: 0x04, size: 1, scaledConfigurationValue: luminanceoff), | |
zwave.configurationV1.configurationGet(parameterNumber: 0x04), | |
// send no-motion report x minutes after motion stops | |
zwave.configurationV1.configurationSet(parameterNumber: 0x05, size: 1, scaledConfigurationValue: MotionRst), | |
zwave.configurationV1.configurationGet(parameterNumber: 0x05), | |
// set motion sensor sensitivity | |
zwave.configurationV1.configurationSet(parameterNumber: 0x06, size: 1, scaledConfigurationValue: PIRsens), | |
zwave.configurationV1.configurationGet(parameterNumber: 0x06), | |
// set LED behavior | |
zwave.configurationV1.configurationSet(parameterNumber: 0x07, size: 1, scaledConfigurationValue: LEDbehav), | |
zwave.configurationV1.configurationGet(parameterNumber: 0x07), | |
// get updated battery and sensor data | |
zwave.batteryV1.batteryGet(), | |
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1), | |
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:3, scale:0), | |
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:5, scale:0), | |
// Can use the zwaveHubNodeId variable to add the hub to the device's associations: | |
zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId) | |
] | |
commands(request) + ["delay 20000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()] | |
} | |
private setConfigured() { | |
updateDataValue("configured", "true") | |
return [] | |
} | |
private 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=1000) { | |
delayBetween(commands.collect{ command(it) }, delay) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment