UPDATE: Got it working!
-The motion sensor was not working because the parameter to enabled was set to zero.
- Not sure why, but the response() function was not pushing the z-wave ConfigurationSet commands back to the device so I made the config action a standalone action.
-I also extended the delay between transactions… that seemed to help the device take the configs.
I have built a much-improved device type of the Gen5 if anyone wants to use it. It is fully configurable and let’s you “beep” it to have it report config values back to the log so you can check if they took or not. Sharing this code so no one has to go through what I did with their gen5.
To configure using this Device Type:
-
Plug sensor into USB so it’s always listening (in range of hub of course)
-
Save the device preferences with all the settings how you want them
-
Press the “configure” device tile and what about a minute
-
Open the log viewer and Press the “get config” device tile to see that your settings took
/**
-
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.
-
Modified by john.fullman@gmail.com
*/
metadata
{
definition (name: “Aeon Multisensor Gen5 (Configurable)”, namespace: “”, author: "john.fullman@gmail.com")
{
capability "Motion Sensor"
capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Illuminance Measurement"
capability "Configuration"
capability "Sensor"
capability "Battery"
capability "Refresh"
capability “Tone” //used for LOG CONFIG buttonfingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x98,0x7A", outClusters:"0x5A"
}
preferences
{
//realtime reporting
input “ReportRealTime”, “bool”, title: “Should values be reported in realtime as they move past thresholds?”, required: true, displayDuringSetup: true, defaultValue: false
input “ReportTemperatureRT”, “decimal”, title: “Report threshold temperature (degrees):”, required: true, displayDuringSetup: true, defaultValue: 1.8
input “ReportHumidityRT”, “decimal”, title: “Report threshold for humidity (%):”, required: true, displayDuringSetup: true, defaultValue: 1.0
input “ReportIlluminanceRT”, “decimal”, title: “Report threshold for illuminance (LUX):”, required: true, displayDuringSetup: true, defaultValue: 100.0
input “ReportBatteryRT”, “decimal”, title: “Report threshold for battery (%):”, required: true, displayDuringSetup: true, defaultValue: 5.0//motion input "MotionStopMinutes", "number", title: "Minutes until no-motion is reported:", required: true, displayDuringSetup: true, defaultValue: 4 input "MotionStopSeconds", "number", title: "Seconds until no-motion is reported:", required: true, displayDuringSetup: true, defaultValue: 0 //group 1 input "ReportInterval1", "number", title: "Report Group 1 items every x minutes:", required: true, displayDuringSetup: true, defaultValue: 30 input "ReportTemperature1", "bool", title: "Report temperature in Group 1?", required: true, displayDuringSetup: true, defaultValue: true input "ReportHumidity1", "bool", title: "Report humidity in Group 1?", required: true, displayDuringSetup: true, defaultValue: true input "ReportIlluminance1", "bool", title: "Report illuminance in Group 1?", required: true, displayDuringSetup: true, defaultValue: true input "ReportBattery1", "bool", title: "Report battery level in Group 1?", required: true, displayDuringSetup: true, defaultValue: true //group 2 input "ReportInterval2", "number", title: "Report Group 2 items every x minutes:", required: true, displayDuringSetup: true, defaultValue: 30 input "ReportTemperature2", "bool", title: "Report temperature in Group 2?", required: true, displayDuringSetup: true, defaultValue: false input "ReportHumidity2", "bool", title: "Report humidity in Group 2?", required: true, displayDuringSetup: true, defaultValue: false input "ReportIlluminance2", "bool", title: "Report illuminance in Group 2?", required: true, displayDuringSetup: true, defaultValue: false input "ReportBattery2", "bool", title: "Report battery level in Group 2?", required: true, displayDuringSetup: true, defaultValue: false //group 3 input "ReportInterval3", "number", title: "Report Group 3 items every x minutes:", required: true, displayDuringSetup: true, defaultValue: 30 input "ReportTemperature3", "bool", title: "Report temperature in Group 3?", required: true, displayDuringSetup: true, defaultValue: false input "ReportHumidity3", "bool", title: "Report humidity in Group 3?", required: true, displayDuringSetup: true, defaultValue: false input "ReportIlluminance3", "bool", title: "Report illuminance in Group 3?", required: true, displayDuringSetup: true, defaultValue: false input "ReportBattery3", "bool", title: "Report battery level in Group 3?", required: true, displayDuringSetup: true, defaultValue: false
}
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”) {
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} lux’, unit:“lux”
}
valueTile(“battery”, “device.battery”, inactiveLabel: false, decoration: “flat”) {
state “battery”, label:’${currentValue}% battery’, unit:""
}
standardTile(“configure”, “device.configure”, inactiveLabel: false, decoration: “flat”) {
state “configure”, label:’’, action:“configure”, icon:“st.secondary.configure”
}
standardTile(“currentconfig”, “tone.beep”)
{
state “default”, label: “log config”, action: “tone.beep” //, inactiveLabel: true
}
standardTile(“refresh”, “device.refresh”, inactiveLabel: false, decoration: “flat”) {
state “default”, label:’’, action:“refresh.refresh”, icon:“st.secondary.refresh”
}main(["motion", "temperature", "humidity", "illuminance"]) details(["motion", "temperature", "humidity", "illuminance", "battery", "configure", "currentconfig", "refresh"])
}
}
def parse(String description)
{
def result = null
if (description == “updated”) {
result = null
} else {
def cmd = zwave.parse(description, [0x31: 5, 0x30: 2, 0x70: 1, 0x84: 2])
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("not sending wakeUpNoMoreInformation yet") //result += response(["delay 20000"] + configure()) } else { result += response(zwave.wakeUpV1.wakeUpNoMoreInformation()) } return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x30: 2, 0x70: 1, 0x84: 2])
// 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) {
// log.debug "Received SecurityCommandsSupportedReport"
response([“delay 10000”] + 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;
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) {
motionEvent(cmd.sensorValue)
}def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
motionEvent(cmd.value)
}def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
if (cmd.notificationType == 7 && cmd.event == 7) {
motionEvent(cmd.notificationStatus)
} else {
createEvent(descriptionText: cmd.toString(), isStateChange: false)
}
}def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
def hexval = cmd.configurationValue.collect { String.format( ‘%02x’, it.toInteger() ) }.join()
def decval = Integer.parseInt(hexval,16)
def precval = decval / 256.0
def minval = (decval / 60).toInteger()
def secval = decval - (minval * 60)
switch(cmd.parameterNumber)
{
case 3: //motion stop seconds
log.info "Will transmit no-motion report after ${minval} miuntes and ${secval} seconds of no motion."
break
case 4: //motion enabled
if(decval == 1)
{
log.info “Motion sensor is enabled.”
}
else
{
log.info “Motion sensor is disabled.”
}
break
case 5: //motion commandset
if(deval == 1)
{
log.info “Sending ‘basic set’ commands for motion.”
}
else if(decval == 2)
{
log.info “Sending ‘sensor binary report’ commands for motion.”
}
else
{
log.info “Unxpected value for motion commandset. Currently 0x${hexval}”
}
break
case 40: //realtime threshold reporting enabled
if(decval == 1)
{
log.info “Realtime (threshold) reporting is enabled.”
}
else
{
log.info “Realtime (threshold) reporting is disabled.”
}
break
case 41: //temperature threshold
def fer_thresh = precval * 1.8
log.info "Temperature threshold is ${precval}C or ${fer_thresh}F."
break
case 42: //humidity threshold
log.info "Humidity threshold is ${precval}%."
break
case 43: //luminance threshold
log.info "Luminance threshold is ${precval} LUX."
break
case 44: //battery threshold
log.info "Battery threshold is ${precval}%."
break
case 101: //group 1 interval
log.info "Group 1 will report every ${minval} miuntes and ${secval} seconds."
break
case 102: //group 2 interval
log.info "Group 2 will report every ${minval} miuntes and ${secval} seconds."
break
case 103: //group 3 interval
log.info "Group 3 will report every ${minval} miuntes and ${secval} seconds."
break
case 111: //group 1 members
log.info "Group 1 report members are ${ReportItemsToString(decval)}"
break
case 112: //group 2 members
log.info "Group 2 report members are ${ReportItemsToString(decval)}"
break
case 113: //group 3 members
log.info "Group 3 report members are ${ReportItemsToString(decval)}"
break
case 252: //config lock
if(decval == 1)
{
log.info “Configuration settings are locked.”
}
else
{
log.info “Configuration settings are unlocked.”
}
break
default:
log.info "Parameter #${cmd.parameterNumber} = 0x${hexval}"
break
}
//log.debug “Parameter #${cmd.parameterNumber} = 0x${hexval} = ${cmd.configurationValue.toString()}”
}def zwaveEvent(physicalgraph.zwave.Command cmd) {
createEvent(descriptionText: cmd.toString(), isStateChange: false)
}def ReportItemsToString(int report_items)
{
def ret = ""
if((report_items & 128) > 0)
{
def item_name = "Luminance"
if(ret == “”)
{
ret = item_name
}
else
{
ret = “${ret}, ${item_name}”
}
}
if((report_items & 64) > 0)
{
def item_name = "Humidity"
if(ret == “”)
{
ret = item_name
}
else
{
ret = “${ret}, ${item_name}”
}
}
if((report_items & 32) > 0)
{
def item_name = "Temperature"
if(ret == “”)
{
ret = item_name
}
else
{
ret = “${ret}, ${item_name}”
}
}
if((report_items & 1) > 0)
{
def item_name = "Battery"
if(ret == “”)
{
ret = item_name
}
else
{
ret = “${ret}, ${item_name}”
}
}
return ret
}def BuildReportGroupConfig(i)
{
int report_interval = 0
int report_items = 0switch(i) { case 1: report_interval = ReportInterval1 if(ReportTemperature1) { report_items = report_items|128 } if(ReportHumidity1) { report_items = report_items|64 } if(ReportIlluminance1) { report_items = report_items|32 } if(ReportBattery1) { report_items = report_items|1 } break; case 2: report_interval = ReportInterval2 if(ReportTemperature2) { report_items = report_items|128 } if(ReportHumidity2) { report_items = report_items|64 } if(ReportIlluminance2) { report_items = report_items|32 } if(ReportBattery2) { report_items = report_items|1 } break; case 3: report_interval = ReportInterval3 if(ReportTemperature3) { report_items = report_items|128 } if(ReportHumidity3) { report_items = report_items|64 } if(ReportIlluminance3) { report_items = report_items|32 } if(ReportBattery3) { report_items = report_items|1 } break; } if(report_interval == 0 || report_items == 0) { log.debug "Report Group ${i} - No Report" return [ zwave.configurationV1.configurationSet(parameterNumber: 100 + i, size: 4, scaledConfigurationValue: 0), zwave.configurationV1.configurationSet(parameterNumber: 110 + i, size: 4, scaledConfigurationValue: 30 * 60) ] } log.debug "Report Group ${i} - Report ${report_items} every ${report_interval} minutes" return [ zwave.configurationV1.configurationSet(parameterNumber: 100 + i, size: 4, scaledConfigurationValue: report_items), zwave.configurationV1.configurationSet(parameterNumber: 110 + i, size: 4, scaledConfigurationValue: report_interval * 60) ]
}
def configure()
{
log.debug “configure()”//calculate parameter values int motion_stop_value = MotionStopMinutes * 60 + MotionStopSeconds int iReportRealTime = ReportRealTime ? 1 : 0 int temp_th = (256.0 * ReportTemperatureRT / 1.8).toInteger() int hum_th = (256.0 * ReportHumidityRT).toInteger() int ill_th = (256.0 * ReportIlluminanceRT).toInteger() int batt_th = (256.0 * ReportBatteryRT).toInteger() //show values being sent log.debug "Report no motion after ${motion_stop_value} seconds" if(iReportRealTime == 1) { log.debug "Realtime reporting enabled with values (P41=${temp_th}; P42=${hum_th}; P43=${ill_th}; P44=${batt_th})" } else { log.debug "Realtime reporting disabled" } def request = GetSensorUpdates() + [ //unlock config in case it is locked zwave.configurationV1.configurationSet(parameterNumber: 252, size: 1, scaledConfigurationValue: 0) //configure report intervals ] + BuildReportGroupConfig(1) + BuildReportGroupConfig(2) + BuildReportGroupConfig(3) + [ //configure realtime reporting zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: iReportRealTime), zwave.configurationV1.configurationSet(parameterNumber: 41, size: 2, scaledConfigurationValue: temp_th), zwave.configurationV1.configurationSet(parameterNumber: 42, size: 2, scaledConfigurationValue: hum_th), zwave.configurationV1.configurationSet(parameterNumber: 43, size: 2, scaledConfigurationValue: ill_th), zwave.configurationV1.configurationSet(parameterNumber: 44, size: 2, scaledConfigurationValue: batt_th), // enable motion reporting zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, scaledConfigurationValue: 1), // send no-motion report x seconds after motion stops zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: motion_stop_value), // send binary sensor report instead of basic set for motion zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: 2), // disable notification-style motion events zwave.notificationV3.notificationSet(notificationType: 7, notificationStatus: 0), ] setConfigured() //return secureSequence(request) //+ ["delay 20000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()] return secureSequence(request, 2000)
}
def setConfigured()
{
device.updateDataValue(“configured”, “true”)
}def isConfigured()
{
Boolean configured = device.getDataValue([“configured”]) as Boolean
return configured
}private secure(physicalgraph.zwave.Command cmd) {
return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}private secureSequence(commands, delay=200)
{
return delayBetween(commands.collect{ secure(it) }, delay)
}def GetSensorUpdates()
{
return [
//sensor updates
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1), //temperature
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 3), //light
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 5), //humidity
zwave.sensorBinaryV2.sensorBinaryGet(sensorType: 12), //motion
zwave.batteryV1.batteryGet() //battery
]
}def GetCurrentConfig()
{
return [
zwave.configurationV1.configurationGet(parameterNumber: 3),
zwave.configurationV1.configurationGet(parameterNumber: 4),
zwave.configurationV1.configurationGet(parameterNumber: 5),
zwave.configurationV1.configurationGet(parameterNumber: 40),
zwave.configurationV1.configurationGet(parameterNumber: 41),
zwave.configurationV1.configurationGet(parameterNumber: 42),
zwave.configurationV1.configurationGet(parameterNumber: 43),
zwave.configurationV1.configurationGet(parameterNumber: 44),
zwave.configurationV1.configurationGet(parameterNumber: 101),
zwave.configurationV1.configurationGet(parameterNumber: 102),
zwave.configurationV1.configurationGet(parameterNumber: 103),
zwave.configurationV1.configurationGet(parameterNumber: 111),
zwave.configurationV1.configurationGet(parameterNumber: 112),
zwave.configurationV1.configurationGet(parameterNumber: 113),
zwave.configurationV1.configurationGet(parameterNumber: 252)
]
}def refresh()
{
log.debug "Sensor data refresh initiated"
return secureSequence(GetSensorUpdates(), 1000)
}def currentconfig()
{
log.debug "Getting current configuration"
return secureSequence(GetCurrentConfig(), 2000)
}def beep()
{
return currentconfig()
} -