diff --git a/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy index 4d7f872..c9c5a30 100644 --- a/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy @@ -22,28 +22,29 @@ metadata { capability "Temperature Measurement" capability "Water Sensor" capability "Health Check" - - fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: "" + + fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: "" + fingerprint mfr:"010F", prod:"0B01", model:"2002" + fingerprint mfr:"010F", prod:"0B01", model:"1002" } simulator { - } - - tiles(scale: 2) { - multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app - tileAttribute("device.water", key:"PRIMARY_CONTROL") { - attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#00a0dc") - attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#e86d13") - } - - tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { + + tiles(scale: 2) { + multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app + tileAttribute("device.water", key:"PRIMARY_CONTROL") { + attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#00a0dc") + attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#e86d13") + } + + tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { attributeState("active", label:'tamper active', backgroundColor:"#00a0dc") attributeState("inactive", label:'tamper inactive', backgroundColor:"#cccccc") - } - } - - valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { + } + } + + valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { state "temperature", label:'${currentValue}°', backgroundColors:[ [value: 31, color: "#153591"], @@ -55,22 +56,22 @@ metadata { [value: 96, color: "#bc2323"] ] } - - valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "battery", label:'${currentValue}% battery', unit:"" - } - - main "FGFS" - details(["FGFS","battery", "temperature"]) - } + + valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + main "FGFS" + details(["FGFS","battery", "temperature"]) + } } // parse events into attributes def parse(String description) { log.debug "Parsing '${description}'" def result = [] - - if (description.startsWith("Err 106")) { + + if (description.startsWith("Err 106")) { if (state.sec) { result = createEvent(descriptionText:description, displayed:false) } else { @@ -85,13 +86,13 @@ def parse(String description) { } else if (description == "updated") { return null } else { - def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72:2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1]) + def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72:2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1]) - if (cmd) { - log.debug "Parsed '${cmd}'" - zwaveEvent(cmd) - } - } + if (cmd) { + log.debug "Parsed '${cmd}'" + zwaveEvent(cmd) + } + } } //security @@ -108,7 +109,7 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat //crc16 def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) { - def versions = [0x31: 5, 0x72: 2, 0x80: 1] + def versions = [0x31: 5, 0x72: 2, 0x80: 1] def version = versions[cmd.commandClass as Integer] def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass) def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data) @@ -122,105 +123,124 @@ def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false) - def cmds = [] - cmds << encap(zwave.batteryV1.batteryGet()) - cmds << "delay 500" - cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)) - cmds << "delay 1200" - cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation()) - [event, response(cmds)] + def cmds = [] + // cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)) + // cmds << "delay 500" + cmds << encap(zwave.batteryV1.batteryGet()) + [event, response(cmds)] } def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { log.debug "manufacturerId: ${cmd.manufacturerId}" - log.debug "manufacturerName: ${cmd.manufacturerName}" - log.debug "productId: ${cmd.productId}" - log.debug "productTypeId: ${cmd.productTypeId}" + log.debug "manufacturerName: ${cmd.manufacturerName}" + log.debug "productId: ${cmd.productId}" + log.debug "productTypeId: ${cmd.productTypeId}" } def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) { log.debug "deviceIdData: ${cmd.deviceIdData}" - log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}" - log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}" - log.debug "deviceIdType: ${cmd.deviceIdType}" - - if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format + log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}" + log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}" + log.debug "deviceIdType: ${cmd.deviceIdType}" + + if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) { //serial number in binary format String serialNumber = "h'" - - cmd.deviceIdData.each{ data -> - serialNumber += "${String.format("%02X", data)}" - } - - updateDataValue("serialNumber", serialNumber) - log.debug "${device.displayName} - serial number: ${serialNumber}" - } + + cmd.deviceIdData.each{ data -> + serialNumber += "${String.format("%02X", data)}" + } + + updateDataValue("serialNumber", serialNumber) + log.debug "${device.displayName} - serial number: ${serialNumber}" + } + + def response_cmds = [] + if (!device.currentState("temperature")) { + response_cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)) + } + if (!getDataValue("version") && !zwaveInfo.ver) { + log.debug "Requesting Version Report" + response_cmds << "delay 500" + response_cmds << encap(zwave.versionV1.versionGet()) + } + response_cmds << "delay 1000" + response_cmds << encap(zwave.wakeUpV2.wakeUpNoMoreInformation()) + [[:], response(response_cmds)] } -def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { - updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}") - log.debug "applicationVersion: ${cmd.applicationVersion}" - log.debug "applicationSubVersion: ${cmd.applicationSubVersion}" - log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}" - log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}" - log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}" +def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}") + log.debug "applicationVersion: ${cmd.applicationVersion}" + log.debug "applicationSubVersion: ${cmd.applicationSubVersion}" + log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}" + log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}" + log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}" } def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + def result = [] def map = [:] map.name = "battery" map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString() map.unit = "%" - map.displayed = true - createEvent(map) + + result << createEvent(map) + + if (!getDataValue("serialNumber")) { + result << response(encap(zwave.manufacturerSpecificV2.deviceSpecificGet())) + } else { + result << response(encap(zwave.wakeUpV2.wakeUpNoMoreInformation())) + } + result } def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { def map = [:] - if (cmd.notificationType == 5) { - switch (cmd.event) { - case 2: - map.name = "water" - map.value = "wet" - map.descriptionText = "${device.displayName} is ${map.value}" - break - - case 0: - map.name = "water" - map.value = "dry" - map.descriptionText = "${device.displayName} is ${map.value}" - break - } - } else if (cmd.notificationType == 7) { - switch (cmd.event) { - case 0: - map.name = "tamper" - map.value = "inactive" - map.descriptionText = "${device.displayName}: tamper alarm has been deactivated" + if (cmd.notificationType == 5) { + switch (cmd.event) { + case 2: + map.name = "water" + map.value = "wet" + map.descriptionText = "${device.displayName} is ${map.value}" break - - case 3: - map.name = "tamper" - map.value = "active" - map.descriptionText = "${device.displayName}: tamper alarm activated" - break - } - } - - createEvent(map) + + case 0: + map.name = "water" + map.value = "dry" + map.descriptionText = "${device.displayName} is ${map.value}" + break + } + } else if (cmd.notificationType == 7) { + switch (cmd.event) { + case 0: + map.name = "tamper" + map.value = "inactive" + map.descriptionText = "${device.displayName}: tamper alarm has been deactivated" + break + + case 3: + map.name = "tamper" + map.value = "active" + map.descriptionText = "${device.displayName}: tamper alarm activated" + break + } + } + + createEvent(map) } def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { def map = [:] if (cmd.sensorType == 1) { - // temperature - def cmdScale = cmd.scale == 1 ? "F" : "C" - map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) - map.unit = getTemperatureScale() - map.name = "temperature" - map.displayed = true + // temperature + def cmdScale = cmd.scale == 1 ? "F" : "C" + map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) + map.unit = getTemperatureScale() + map.name = "temperature" + map.displayed = true } - - createEvent(map) + + createEvent(map) } def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) { @@ -229,21 +249,18 @@ def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLoca def configure() { log.debug "Executing 'configure'" - // Device-Watch simply pings if no device events received for 8 hrs & 2 minutes + // Device wakes up every 4 hours, this interval of 8h 2m allows us to miss one wakeup notification before marking offline sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - def cmds = [] - - cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGFS' default wake up interval - cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet() - cmds += zwave.manufacturerSpecificV2.deviceSpecificGet() - cmds += zwave.versionV1.versionGet() - cmds += zwave.batteryV1.batteryGet() - cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0) - cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId]) - cmds += zwave.wakeUpV2.wakeUpNoMoreInformation() + // default initial state + sendEvent(name: "water", value: "dry") - encapSequence(cmds, 500) + def cmds = [] + + cmds << zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId]) + cmds << zwave.batteryV1.batteryGet() // other queries sent as response to BatteryReport + + encapSequence(cmds, 200) } private secure(physicalgraph.zwave.Command cmd) { @@ -252,7 +269,7 @@ private secure(physicalgraph.zwave.Command cmd) { private crc16(physicalgraph.zwave.Command cmd) { //zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format() - "5601${cmd.format()}0000" + "5601${cmd.format()}0000" } private encapSequence(commands, delay=200) { @@ -260,13 +277,10 @@ private encapSequence(commands, delay=200) { } private encap(physicalgraph.zwave.Command cmd) { - def secureClasses = [0x20, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C] - - //todo: check if secure inclusion was successful - //if not do not send security-encapsulated command - if (secureClasses.find{ it == cmd.commandClassId }) { - secure(cmd) - } else { - crc16(cmd) - } -} \ No newline at end of file + if (zwaveInfo.zw && !zwaveInfo.zw.contains("s")) { + // Secure inclusion failed + crc16(cmd) + } else { + secure(cmd) + } +} diff --git a/devicetypes/smartthings/fibaro-flood-sensor.src/fibaro-flood-sensor.groovy b/devicetypes/smartthings/fibaro-flood-sensor.src/fibaro-flood-sensor.groovy index 9dc3ab2..fa3b173 100644 --- a/devicetypes/smartthings/fibaro-flood-sensor.src/fibaro-flood-sensor.groovy +++ b/devicetypes/smartthings/fibaro-flood-sensor.src/fibaro-flood-sensor.groovy @@ -1,12 +1,12 @@ /** * Device Type Definition File * - * Device Type: Fibaro Flood Sensor - * File Name: fibaro-flood-sensor.groovy - * Initial Release: 2014-12-10 - * @author: Todd Wackford - * Email: todd@wackford.net - * @version: 1.0 + * Device Type: Fibaro Flood Sensor + * File Name: fibaro-flood-sensor.groovy + * Initial Release: 2014-12-10 + * @author: Todd Wackford + * Email: todd@wackford.net + * @version: 1.0 * * Copyright 2014 SmartThings * @@ -26,8 +26,8 @@ * not displayed to the user. We do this so we can receive events and display on device * activity. If the user wants to display the tamper tile, adjust the tile display lines * with the following: - * main(["water", "temperature", "tamper"]) - * details(["water", "temperature", "battery", "tamper"]) + * main(["water", "temperature", "tamper"]) + * details(["water", "temperature", "battery", "tamper"]) * * @param none * @@ -40,14 +40,17 @@ metadata { capability "Configuration" capability "Battery" capability "Health Check" - capability "Sensor" - - command "resetParams2StDefaults" - command "listCurrentParams" - command "updateZwaveParam" - command "test" - + capability "Sensor" + + command "resetParams2StDefaults" + command "listCurrentParams" + command "updateZwaveParam" + command "test" + fingerprint deviceId: "0xA102", inClusters: "0x30,0x9C,0x60,0x85,0x8E,0x72,0x70,0x86,0x80,0x84" + fingerprint mfr:"010F", prod:"0000", model:"2002" + fingerprint mfr:"010F", prod:"0000", model:"1002" + fingerprint mfr:"010F", prod:"0B00", model:"1001" } simulator { @@ -88,9 +91,9 @@ metadata { [value: 96, color: "#bc2323"] ] } - standardTile("tamper", "device.tamper") { - state("secure", label:"secure", icon:"st.locks.lock.locked", backgroundColor:"#ffffff") - state("tampered", label:"tampered", icon:"st.locks.lock.unlocked", backgroundColor:"#53a7c0") + standardTile("tamper", "device.tamper") { + state("secure", label:"secure", icon:"st.locks.lock.locked", backgroundColor:"#ffffff") + state("tampered", label:"tampered", icon:"st.locks.lock.unlocked", backgroundColor:"#53a7c0") } valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") { state "battery", label:'${currentValue}% battery', unit:"" @@ -108,26 +111,17 @@ metadata { def parse(String description) { def result = [] - - if (description == "updated") { - if (!state.MSR) { - result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds: 60*60, nodeid:zwaveHubNodeId)) - result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet()) - } - } else { - def cmd = zwave.parse(description, [0x31: 2, 0x30: 1, 0x70: 2, 0x71: 1, 0x84: 1, 0x80: 1, 0x9C: 1, 0x72: 2, 0x56: 2, 0x60: 3]) - - if (cmd) { - result += zwaveEvent(cmd) //createEvent(zwaveEvent(cmd)) - } - } - - result << response(zwave.batteryV1.batteryGet().format()) - - if ( result[0] != null ) { + + def cmd = zwave.parse(description, [0x31: 2, 0x30: 1, 0x70: 2, 0x71: 1, 0x84: 1, 0x80: 1, 0x9C: 1, 0x72: 2, 0x56: 2, 0x60: 3]) + + if (cmd) { + result += zwaveEvent(cmd) //createEvent(zwaveEvent(cmd)) + } + + if ( result[0] != null ) { log.debug "Parse returned ${result}" result - } + } } @@ -144,10 +138,9 @@ 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 - result += lateConfigure(true) + result << lateConfigure(true) } else { - result += response(zwave.wakeUpV1.wakeUpNoMoreInformation()) - log.debug "We're done with WakeUp!" + result << response(zwave.wakeUpV1.wakeUpNoMoreInformation()) } result } @@ -155,7 +148,7 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) { def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) { def map = [:] - + switch (cmd.sensorType) { case 1: // temperature @@ -186,7 +179,7 @@ def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cm def map = [:] map.value = cmd.sensorValue ? "active" : "inactive" map.name = "acceleration" - + if (map.value == "active") { map.descriptionText = "$device.displayName detected vibration" } @@ -201,49 +194,49 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { - log.debug "BasicSet with CMD = ${cmd}" - - if (!isConfigured()) { - def result = [] - def map = [:] - - map.name = "water" + log.debug "BasicSet with CMD = ${cmd}" + + if (!isConfigured()) { + def result = [] + def map = [:] + + map.name = "water" map.value = cmd.value ? "wet" : "dry" map.descriptionText = "${device.displayName} is ${map.value}" - - // If we are getting a BasicSet, and isConfigured == false, then we are likely NOT properly configured. - result += lateConfigure(true) - - result << createEvent(map) - - result - } + + // If we are getting a BasicSet, and isConfigured == false, then we are likely NOT properly configured. + result += lateConfigure(true) + + result << createEvent(map) + + result + } } def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) { def map = [:] - + if (cmd.sensorType == 0x05) { map.name = "water" map.value = cmd.sensorState ? "wet" : "dry" map.descriptionText = "${device.displayName} is ${map.value}" - - log.debug "CMD = SensorAlarmReport: ${cmd}" - setConfigured() - } else if ( cmd.sensorType == 0) { - map.name = "tamper" - map.isStateChange = true - map.value = cmd.sensorState ? "tampered" : "secure" - map.descriptionText = "${device.displayName} has been tampered with" - runIn(30, "resetTamper") //device does not send alarm cancelation - - } else if ( cmd.sensorType == 1) { - map.name = "tamper" - map.value = cmd.sensorState ? "tampered" : "secure" - map.descriptionText = "${device.displayName} has been tampered with" - runIn(30, "resetTamper") //device does not send alarm cancelation - + + log.debug "CMD = SensorAlarmReport: ${cmd}" + setConfigured() + } else if ( cmd.sensorType == 0) { + map.name = "tamper" + map.isStateChange = true + map.value = cmd.sensorState ? "tampered" : "secure" + map.descriptionText = "${device.displayName} has been tampered with" + runIn(30, "resetTamper") //device does not send alarm cancelation + + } else if ( cmd.sensorType == 1) { + map.name = "tamper" + map.value = cmd.sensorState ? "tampered" : "secure" + map.descriptionText = "${device.displayName} has been tampered with" + runIn(30, "resetTamper") //device does not send alarm cancelation + } else { map.descriptionText = "${device.displayName}: ${cmd}" } @@ -252,10 +245,10 @@ def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) def resetTamper() { def map = [:] - map.name = "tamper" - map.value = "secure" - map.descriptionText = "$device.displayName is secure" - sendEvent(map) + map.name = "tamper" + map.value = "secure" + map.descriptionText = "$device.displayName is secure" + sendEvent(map) } def zwaveEvent(physicalgraph.zwave.Command cmd) { @@ -269,10 +262,10 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) log.debug "msr: $msr" device.updateDataValue(["MSR", msr]) - - if ( msr == "010F-0B00-2001" ) { //this is the msr and device type for the fibaro flood sensor - result += lateConfigure(true) - } + + if ( msr == "010F-0B00-2001" ) { //this is the msr and device type for the fibaro flood sensor + result += lateConfigure(true) + } result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false) result @@ -284,17 +277,17 @@ def setConfigured() { def isConfigured() { Boolean configured = device.getDataValue(["configured"]) as Boolean - - return configured + + return configured } def lateConfigure(setConf = False) { def res = response(configure()) - - if (setConf) - setConfigured() - - return res + + if (setConf) + setConfigured() + + return res } /** @@ -306,29 +299,34 @@ def lateConfigure(setConf = False) { */ def configure() { log.debug "Configuring Device..." - // Device-Watch simply pings if no device events received for 8 hrs & 2 minutes + // Device wakes up every 4 hours, this interval allows us to miss one wakeup notification before marking offline sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - def cmds = [] - - // send associate to group 2 to get alarm data - cmds << zwave.associationV2.associationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format() - - cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format() - - // send associate to group 3 to get sensor data reported only to hub - cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format() + // default initial state + sendEvent(name: "water", value: "dry") - // temp hysteresis set to .5 degrees celcius - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format() + def cmds = [] + + // send associate to group 2 to get alarm data + cmds << zwave.associationV2.associationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format() + + cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format() + + // send associate to group 3 to get sensor data reported only to hub + cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format() // reporting frequency of temps and battery set to one hour cmds << zwave.configurationV1.configurationSet(configurationValue: [0,60*60], parameterNumber: 10, size: 2).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() - - cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format() - + // cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() + + // temp hysteresis set to .5 degrees celcius + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format() + // cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format() + + cmds << zwave.batteryV1.batteryGet().format() + + cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format() + delayBetween(cmds, 100) } @@ -354,18 +352,18 @@ def test() { * @return none */ def updateZwaveParam(params) { - if ( params ) { - def pNumber = params.paramNumber - def pSize = params.size - def pValue = [params.value] - log.debug "Make sure device is awake and in recieve mode (triple-click?)" - log.debug "Updating ${device.displayName} parameter number '${pNumber}' with value '${pValue}' with size of '${pSize}'" + if ( params ) { + def pNumber = params.paramNumber + def pSize = params.size + def pValue = [params.value] + log.debug "Make sure device is awake and in recieve mode (triple-click?)" + log.debug "Updating ${device.displayName} parameter number '${pNumber}' with value '${pValue}' with size of '${pSize}'" def cmds = [] - cmds << zwave.configurationV1.configurationSet(configurationValue: pValue, parameterNumber: pNumber, size: pSize).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: pNumber).format() - delayBetween(cmds, 1000) - } + cmds << zwave.configurationV1.configurationSet(configurationValue: pValue, parameterNumber: pNumber, size: pSize).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: pNumber).format() + delayBetween(cmds, 1000) + } } /** @@ -382,26 +380,26 @@ def updateZwaveParam(params) { def resetParams2StDefaults() { log.debug "Resetting ${device.displayName} parameters to SmartThings compatible defaults" def cmds = [] - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 1, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 2, size: 1).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 7, size: 1).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 9, size: 1).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,60*60], parameterNumber: 10, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 13, size: 1).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [5,220], parameterNumber: 50, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [13,172], parameterNumber: 51, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0,0,225], parameterNumber: 61, size: 4).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,255,0,0], parameterNumber: 62, size: 4).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 63, size: 1).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 73, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 74, size: 1).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 75, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 76, size: 2).format() - cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 77, size: 1).format() - - delayBetween(cmds, 1200) + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 1, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 2, size: 1).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 7, size: 1).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 9, size: 1).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,60*60], parameterNumber: 10, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 13, size: 1).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [5,220], parameterNumber: 50, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [13,172], parameterNumber: 51, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0,0,225], parameterNumber: 61, size: 4).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,255,0,0], parameterNumber: 62, size: 4).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 63, size: 1).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 73, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 74, size: 1).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 75, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 76, size: 2).format() + cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 77, size: 1).format() + + delayBetween(cmds, 1200) } /** @@ -418,25 +416,25 @@ def resetParams2StDefaults() { def listCurrentParams() { log.debug "Listing of current parameter settings of ${device.displayName}" def cmds = [] - cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 13).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 50).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 51).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 61).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 62).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 63).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 73).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 74).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 75).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 76).format() - cmds << zwave.configurationV1.configurationGet(parameterNumber: 77).format() - + cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 13).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 50).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 51).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 61).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 62).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 63).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 73).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 74).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 75).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 76).format() + cmds << zwave.configurationV1.configurationGet(parameterNumber: 77).format() + delayBetween(cmds, 1200) } diff --git a/devicetypes/smartthings/zll-white-color-temperature-bulb-5000k.src/zll-white-color-temperature-bulb-5000k.groovy b/devicetypes/smartthings/zll-white-color-temperature-bulb-5000k.src/zll-white-color-temperature-bulb-5000k.groovy new file mode 100644 index 0000000..3c88380 --- /dev/null +++ b/devicetypes/smartthings/zll-white-color-temperature-bulb-5000k.src/zll-white-color-temperature-bulb-5000k.groovy @@ -0,0 +1,169 @@ +/** + * Copyright 2016 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. + * + */ +import groovy.transform.Field + +@Field Boolean hasConfiguredHealthCheck = false + +metadata { + definition (name: "ZLL White Color Temperature Bulb 5000K", namespace: "smartthings", author: "SmartThings") { + + capability "Actuator" + capability "Color Temperature" + capability "Configuration" + capability "Polling" + capability "Refresh" + capability "Switch" + capability "Switch Level" + capability "Health Check" + + attribute "colorName", "string" + command "setGenericName" + + fingerprint profileId: "C05E", deviceId: "0220", inClusters: "0000, 0004, 0003, 0006, 0008, 0005, 0300", outClusters: "0019", manufacturer: "Eaton", model: "Halo_LT01", deviceJoinName: "Halo RL56 Wireless" + } + + // UI tile definitions + tiles(scale: 2) { + multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ + tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { + attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff" + attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" + attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff" + attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" + } + tileAttribute ("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action:"switch level.setLevel" + } + tileAttribute ("colorName", key: "SECONDARY_CONTROL") { + attributeState "colorName", label:'${currentValue}' + } + } + + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + + controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2700..5000)") { + state "colorTemperature", action:"color temperature.setColorTemperature" + } + valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "colorTemperature", label: '${currentValue} K' + } + + main(["switch"]) + details(["switch", "colorTempSliderControl", "colorTemp", "refresh"]) + } +} + +// Parse incoming device messages to generate events +def parse(String description) { + log.debug "description is $description" + def event = zigbee.getEvent(description) + if (event) { + if (event.name == "colorTemperature") { + event.unit = "K" + } + sendEvent(event) + } + else { + log.warn "DID NOT PARSE MESSAGE for description : $description" + log.debug zigbee.parseDescriptionAsMap(description) + } +} + +def off() { + zigbee.off() + ["delay 1500"] + zigbee.onOffRefresh() +} + +def on() { + zigbee.on() + ["delay 1500"] + zigbee.onOffRefresh() +} + +def setLevel(value) { + zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() +} + +def refresh() { + def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + + // Do NOT config if the device is the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, and maybe other weird things with the others + if (!((device.getDataValue("manufacturer") == "Eaton") && (device.getDataValue("model") == "Halo_LT01"))) { + cmds = cmds + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + } + + cmds +} + +def poll() { + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return zigbee.levelRefresh() +} + +def healthPoll() { + log.debug "healthPoll()" + def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() + cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))} +} + +def configureHealthCheck() { + Integer hcIntervalMinutes = 12 + if (!hasConfiguredHealthCheck) { + log.debug "Configuring Health Check, Reporting" + unschedule("healthPoll") + runEvery5Minutes("healthPoll") + // Device-Watch allows 2 check-in misses from device + sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + hasConfiguredHealthCheck = true + } +} + +def configure() { + log.debug "configure()" + configureHealthCheck() + // Implementation note: for the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, so be sure this is before the call to onOffRefresh + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() +} + +def updated() { + log.debug "updated()" + configureHealthCheck() +} + +def setColorTemperature(value) { + setGenericName(value) + zigbee.setColorTemperature(value) + ["delay 1500"] + zigbee.colorTemperatureRefresh() +} + +//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature +def setGenericName(value){ + if (value != null) { + def genericName = "" + if (value < 3300) { + genericName = "Soft White" + } else if (value < 4150) { + genericName = "Moonlight" + } else if (value <= 5000) { + genericName = "Cool White" + } else { + genericName = "Daylight" + } + sendEvent(name: "colorName", value: genericName, displayed: false) + } +}