Compare commits

...

1 Commits

View File

@@ -1,300 +1,518 @@
/**
* Fibaro Flood Sensor ZW5
*
* Copyright 2016 Fibar Group S.A.
*
* 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: "Fibaro Flood Sensor ZW5", namespace: "fibargroup", author: "Fibar Group S.A.", ocfDeviceType: "x.com.st.d.sensor.moisture") {
capability "Battery"
capability "Configuration"
capability "Sensor"
capability "Tamper Alert"
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 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:"#ffffff")
attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#00a0dc")
}
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("detected", label:'tampered', backgroundColor:"#cccccc")
attributeState("clear", label:'tamper clear', backgroundColor:"#00A0DC")
}
}
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", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
main "FGFS"
details(["FGFS","battery", "temperature"])
}
}
def installed() {
sendEvent(name: "tamper", value: "clear", displayed: false)
}
def updated() {
def tamperValue = device.latestValue("tamper")
if (tamperValue == "active") {
sendEvent(name: "tamper", value: "detected", displayed: false)
} else if (tamperValue == "inactive") {
sendEvent(name: "tamper", value: "clear", displayed: false)
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
def result = []
if (description.startsWith("Err 106")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
result = createEvent(
descriptionText: "FGFS failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
} 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])
if (cmd) {
log.debug "Parsed '${cmd}'"
zwaveEvent(cmd)
}
}
}
//security
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
createEvent(descriptionText: cmd.toString())
}
}
//crc16
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
{
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)
if (!encapsulatedCommand) {
log.debug "Could not extract command from $cmd"
} else {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
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}"
}
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
String serialNumber = "h'"
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))
}
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.batteryv1.BatteryReport cmd) {
def result = []
def map = [:]
map.name = "battery"
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
map.unit = "%"
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 = "clear"
map.descriptionText = "Tamper aleart cleared"
break
case 3:
map.name = "tamper"
map.value = "detected"
map.descriptionText = "Tamper alert: sensor removed or covering opened"
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
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
log.info "${device.displayName}: received command: $cmd - device has reset itself"
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
log.debug "Catchall reached for cmd: $cmd"
}
def configure() {
log.debug "Executing 'configure'"
// 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])
// default initial state
sendEvent(name: "water", value: "dry")
def cmds = []
cmds << zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
cmds << zwave.batteryV1.batteryGet() // other queries sent as response to BatteryReport
cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1)
encapSequence(cmds, 200)
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private crc16(physicalgraph.zwave.Command cmd) {
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
"5601${cmd.format()}0000"
}
private encapSequence(commands, delay=200) {
delayBetween(commands.collect{ encap(it) }, delay)
}
private encap(physicalgraph.zwave.Command cmd) {
if (zwaveInfo.zw && !zwaveInfo.zw.contains("s")) {
// Secure inclusion failed
crc16(cmd)
} else {
secure(cmd)
}
}
/**
* Fibaro Flood Sensor ZW5
*/
metadata {
definition(name: "Fibaro Flood Sensor ZW5", namespace: "FibarGroup", author: "Fibar Group") {
capability "Battery"
capability "Configuration"
capability "Sensor"
capability "Tamper Alert"
capability "Temperature Measurement"
capability "Water Sensor"
capability "Power Source"
attribute "syncStatus", "string"
attribute "lastAlarmDate", "string"
command "forceSync"
fingerprint mfr: "010F", prod: "0B01", model: "1002"
fingerprint mfr: "010F", prod: "0B01", model: "2002"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x59, 0x80, 0x56, 0x7A, 0x73, 0x72, 0x31, 0x98, 0x86, 0x85, 0x20, 0x70, 0x5A, 0x8E, 0x71, 0x9C", outClusters: ""
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x59, 0x80, 0x56, 0x7A, 0x73, 0x72, 0x31, 0x98, 0x86", outClusters: "0x85, 0x20, 0x70, 0x5A, 0x8E, 0x71, 0x9C, 0x84"
}
tiles(scale: 2) {
multiAttributeTile(name: "FGFS", type: "lighting", width: 6, height: 4) {
tileAttribute("device.water", key: "PRIMARY_CONTROL") {
attributeState("dry", label: "Alarm not detected", icon: "http://fibaro-smartthings.s3-eu-west-1.amazonaws.com/flood/flood0sensor.png", backgroundColor: "#79b821")
attributeState("wet", label: "Alarm detected", icon: "http://fibaro-smartthings.s3-eu-west-1.amazonaws.com/flood/flood1sensor.png", backgroundColor: "#ffa81e")
}
tileAttribute("device.multiStatus", key: "SECONDARY_CONTROL") {
attributeState("multiStatus", label: '${currentValue}')
}
}
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("batteryStatus", "device.batteryStatus", inactiveLabel: false, decoration: "flat", width: 4, height: 2) {
state "val", label: '${currentValue}'
}
standardTile("syncStatus", "device.syncStatus", decoration: "flat", width: 2, height: 2) {
def syncIconUrl = "http://fibaro-smartthings.s3-eu-west-1.amazonaws.com/keyfob/sync_icon.png"
state "synced", label: 'OK', action: "forceSync", backgroundColor: "#00a0dc", icon: syncIconUrl
state "pending", label: "Pending", action: "forceSync", backgroundColor: "#153591", icon: syncIconUrl
state "inProgress", label: "Syncing", action: "forceSync", backgroundColor: "#44b621", icon: syncIconUrl
state "incomplete", label: "Incomplete", action: "forceSync", backgroundColor: "#f1d801", icon: syncIconUrl
state "failed", label: "Failed", action: "forceSync", backgroundColor: "#bc2323", icon: syncIconUrl
state "force", label: "Force", action: "forceSync", backgroundColor: "#e86d13", icon: syncIconUrl
}
main "FGFS"
details(["FGFS", "batteryStatus", "temperature", "syncStatus"])
}
preferences {
input(
title: "Fibaro Flood Sensor ZW5 manual",
description: "Tap to view the manual.",
image: "http://manuals.fibaro.com/wp-content/uploads/2017/02/fs_icon.png",
url: "http://manuals.fibaro.com/content/manuals/en/FGFS-101/FGFS-101-EN-T-v2.1.pdf",
type: "href",
element: "href"
)
parameterMap().each {
getPrefsFor(it)
}
input ( name: "logging", title: "Logging", type: "boolean", required: false )
}
}
//UI Support functions
def getPrefsFor(parameter) {
input(
title: "${parameter.num}. ${parameter.title}",
description: parameter.descr,
type: "paragraph",
element: "paragraph"
)
input(
name: parameter.key,
title: null,
type: parameter.type,
options: parameter.options,
range: (parameter.min != null && parameter.max != null) ? "${parameter.min}..${parameter.max}" : null,
defaultValue: parameter.def,
required: false
)
}
def updated() {
if (state.lastUpdated && (now() - state.lastUpdated) < 2000) return
logging("${device.displayName} - Executing updated()", "info")
def cmds = []
def cmdCount = 0
parameterMap().each {
if (settings."$it.key" == null || state."$it.key" == null) {
state."$it.key" = [value: it.def as Integer, state: "notSynced"]
}
if (settings."$it.key" != null) {
if (state."$it.key".value != settings."$it.key" as Integer || state."$it.key".state == "notSynced") {
state."$it.key".value = settings."$it.key" as Integer
state."$it.key".state = "notSynced"
cmdCount = cmdCount + 1
}
} else {
if (state."$it.key".state == "notSynced") {
cmdCount = cmdCount + 1
}
}
}
if (cmdCount > 0) {
logging("${device.displayName} - sending config.", "info")
sendEvent(name: "syncStatus", value: "pending")
}
state.lastUpdated = now()
}
def forceSync() {
if (device.currentValue("syncStatus") != "force") {
state.prevSyncState = device.currentValue("syncStatus")
sendEvent(name: "syncStatus", value: "force")
} else {
if (state.prevSyncState != null) {
sendEvent(name: "syncStatus", value: state.prevSyncState)
} else {
sendEvent(name: "syncStatus", value: "synced")
}
}
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
log.debug "WakeUpNotification"
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
def cmds = []
def cmdsSet = []
def cmdsGet = []
def cmdCount = 0
def results = [createEvent(descriptionText: "$device.displayName woke up", isStateChange: true)]
cmdsGet << zwave.batteryV1.batteryGet()
cmdsGet << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
if (device.currentValue("syncStatus") != "synced") {
parameterMap().each {
if (device.currentValue("syncStatus") == "force") {
state."$it.key".state = "notSynced"
}
if (state."$it.key"?.value != null && state."$it.key"?.state == "notSynced") {
cmdsSet << zwave.configurationV2.configurationSet(configurationValue: intToParam(state."$it.key".value, it.size), parameterNumber: it.num, size: it.size)
cmdsGet << zwave.configurationV2.configurationGet(parameterNumber: it.num)
cmdCount = cmdCount + 1
}
}
log.debug "Not synced, syncing ${cmdCount} parameters"
sendEvent(name: "syncStatus", value: "inProgress")
runIn((5 + cmdCount * 1.5), syncCheck)
}
if (cmdsSet) {
cmds = encapSequence(cmdsSet, 500)
cmds << "delay 500"
}
cmds = cmds + encapSequence(cmdsGet, 1000)
cmds << "delay " + (5000 + cmdCount * 1500)
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
results = results + response(cmds)
return results
}
def syncCheck() {
logging("${device.displayName} - Executing syncCheck()", "info")
def notSynced = []
def count = 0
if (device.currentValue("syncStatus") != "synced") {
parameterMap().each {
if (state."$it.key"?.state == "notSynced") {
notSynced << it
logging "Sync failed! Verify parameter: ${notSynced[0].num}"
logging "Sync $it.key " + state."$it.key"
sendEvent(name: "batteryStatus", value: "Sync incomplited! Check parameter nr. ${notSynced[0].num}")
count = count + 1
}
}
}
if (count == 0) {
logging("${device.displayName} - Sync Complete", "info")
sendEvent(name: "syncStatus", value: "synced")
} else {
logging("${device.displayName} Sync Incomplete", "info")
if (device.currentValue("syncStatus") != "failed") {
sendEvent(name: "syncStatus", value: "incomplete")
}
}
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "ManufacturerSpecificReport"
log.debug "manufacturerId: ${cmd.manufacturerId}"
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 "DeviceSpecificReport"
log.debug "deviceIdData: ${cmd.deviceIdData}"
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
log.debug "deviceIdType: ${cmd.deviceIdType}"
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
log.debug "VersionReport"
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) {
log.debug "VersionReport"
def timeDate = new Date().format("yyyy MMM dd EEE HH:mm:ss", location.timeZone)
sendEvent(name: "batteryStatus", value: "Battery: $cmd.batteryLevel%\n($timeDate)")
}
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
log.debug "NotificationReport"
def map = [:]
def alarmInfo = "Last alarm detection: "
if (cmd.notificationType == 5) {
switch (cmd.event) {
case 2:
map.name = "water"
map.value = "wet"
map.descriptionText = "${device.displayName} is ${map.value}"
state.lastAlarmDate = "\n"+new Date().format("yyyy MMM dd EEE HH:mm:ss", location.timeZone)
multiStatusEvent(alarmInfo + state.lastAlarmDate)
break
case 0:
map.name = "water"
map.value = "dry"
map.descriptionText = "${device.displayName} is ${map.value}"
multiStatusEvent(alarmInfo + state.lastAlarmDate)
break
}
} else if (cmd.notificationType == 7) {
switch (cmd.event) {
case 0:
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
sendEvent(name: "batteryStatus", value: "Tamper alarm inactive")
break
case 3:
map.descriptionText = "${device.displayName}: tamper alarm activated"
sendEvent(name: "batteryStatus", value: "Tamper alarm activated")
break
}
}
createEvent(map)
}
private multiStatusEvent(String statusValue, boolean force = false, boolean display = false) {
if (!device.currentValue("multiStatus")?.contains("Sync") || device.currentValue("multiStatus") == "Sync OK." || force) {
sendEvent(name: "multiStatus", value: statusValue, descriptionText: statusValue, displayed: display)
}
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
log.debug "SensorMultilevelReport"
def tempValue
def map = [:]
if (cmd.sensorType == 1) {
// temperature
def cmdScale = cmd.scale == 1 ? "F" : "C"
tempValue = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, 1)
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, 1)
map.unit = getTemperatureScale()
map.name = "temperature"
map.displayed = true
}
sendEvent(name: "temperature", value: "$tempValue")
log.debug "Temperature:" + tempValue
}
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
log.warn "Test10: DeviceResetLocallyNotification"
log.info "${device.displayName}: received command: $cmd - device has reset itself"
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) {
log.warn cmd
}
/*
####################
## Z-Wave Toolkit ##
####################
*/
def parse(String description) {
def result = []
logging("${device.displayName} - Parsing: ${description}")
if (description.startsWith("Err 106")) {
result = createEvent(
descriptionText: "Failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
} else if (description == "updated") {
return null
} else {
def cmd = zwave.parse(description, cmdVersions())
if (cmd) {
logging("${device.displayName} - Parsed: ${cmd}")
zwaveEvent(cmd)
}
}
}
//event handlers related to configuration and sync
def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) {
def paramKey = parameterMap().find({ it.num == cmd.parameterNumber }).key
logging("${device.displayName} - Parameter ${paramKey} value is ${cmd.scaledConfigurationValue} expected " + state."$paramKey".value, "info")
if (state."$paramKey".value == cmd.scaledConfigurationValue) {
state."$paramKey".state = "synced"
}
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand(cmdVersions())
if (encapsulatedCommand) {
logging("${device.displayName} - Parsed SecurityMessageEncapsulation into: ${encapsulatedCommand}")
zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract Secure command from $cmd"
}
}
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) {
def version = cmdVersions()[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)
if (encapsulatedCommand) {
logging("${device.displayName} - Parsed Crc16Encap into: ${encapsulatedCommand}")
zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract CRC16 command from $cmd"
}
}
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand(cmdVersions())
if (encapsulatedCommand) {
logging("${device.displayName} - Parsed MultiChannelCmdEncap ${encapsulatedCommand}")
zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer)
} else {
log.warn "Unable to extract MultiChannel command from $cmd"
}
}
private logging(text, type = "debug") {
if (settings.logging == "true") {
log."$type" text
}
}
private secEncap(physicalgraph.zwave.Command cmd) {
logging("${device.displayName} - encapsulating command using Secure Encapsulation, command: $cmd", "info")
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private crcEncap(physicalgraph.zwave.Command cmd) {
logging("${device.displayName} - encapsulating command using CRC16 Encapsulation, command: $cmd", "info")
zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
}
private multiEncap(physicalgraph.zwave.Command cmd, Integer ep) {
logging("${device.displayName} - encapsulating command using MultiChannel Encapsulation, ep: $ep command: $cmd", "info")
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: ep).encapsulate(cmd)
}
private encap(physicalgraph.zwave.Command cmd, Integer ep) {
encap(multiEncap(cmd, ep))
}
private encap(List encapList) {
encap(encapList[0], encapList[1])
}
private encap(Map encapMap) {
encap(encapMap.cmd, encapMap.ep)
}
private encap(physicalgraph.zwave.Command cmd) {
if (zwaveInfo.zw.contains("s")) {
secEncap(cmd)
} else if (zwaveInfo.cc.contains("56")) {
crcEncap(cmd)
} else {
logging("${device.displayName} - no encapsulation supported for command: $cmd", "info")
cmd.format()
}
}
private encapSequence(cmds, Integer delay = 250) {
delayBetween(cmds.collect { encap(it) }, delay)
}
private List intToParam(Long value, Integer size = 1) {
def result = []
size.times {
result = result.plus(0, (value & 0xFF) as Short)
value = (value >> 8)
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationRejectedRequest cmd) {
log.warn "Flood Sensor rejected configuration!"
sendEvent(name: "syncStatus", value: "failed")
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify cmd) {
log.debug cmd
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecuritySchemeReport cmd) {
log.debug cmd
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
log.debug cmd
}
/*
##########################
## Device Configuration ##
##########################
*/
/* 0x31 : 5 - Sensor Multilevel
0x56 : 1 - Crc16 Encap
0x71 : 2 - Notification ST supported V3
0x72 : 2 - Manufacturer Specific
0x80 : 1 - Battery
0x84: 2 - Wake Up
0x85: 2 - Association
0x86: 1 - Version
0x98: 1 - Security */
private Map cmdVersions() {
[0x31: 5, 0x56: 1, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1]
}
def configure() {
state.lastAlarmDate = "-"
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()
encapSequence(cmds, 500)
}
private parameterMap() {
[
[key: "AlarmCancellationDelay", num: 1, size: 2, type: "number", def: 0, min: 0, max: 3600, title: "Alarm cancellation delay", descr: "Time period by which a Flood Sensor will retain the flood state after the flooding itself has ceased. 0-3600 (in seconds)"],
[key: "AcousticVisualSignals", num: 2, size: 1, type: "enum", options: [
0: "acoustic and visual alarms inactive",
1: "acoustic alarm inactive, visual alarm active",
2: "acoustic alarm active, visual alarm inactive",
3: "acoustic and visual alarms active"],
def: 3, title: "Acoustic and visual signals on / off in case of flooding.", descr: ""],
[key: "tempInterval", num: 10, size: 4, type: "number", def: 300, min: 1, max: 65535, title: "Interval of temperature measurements", descr: "How often the temperature will be measured (1-65535 in seconds)"],
[key: "floodSensorOnOff", num: 77, size: 1, type: "enum", options: [
0: "on",
1: "off"],
def: 0, title: "Flood sensor functionality turned on/off", descr: "Allows to turn off the internal flood sensor. Tamper and built in temperature sensor will remain active."]
]
}