mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-25 05:04:09 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
215878ddc1 | ||
|
|
42865abc55 | ||
|
|
dc1c78391e | ||
|
|
e279172383 | ||
|
|
0deb26810d | ||
|
|
d4fb75cc47 | ||
|
|
2276748a91 | ||
|
|
8d423e7c4b | ||
|
|
0dfbddee38 | ||
|
|
40f88fa436 | ||
|
|
c3c8bafef4 | ||
|
|
1b9f758bd6 | ||
|
|
ef1b04c08a | ||
|
|
9cece36d69 | ||
|
|
be220e02b2 | ||
|
|
131cc7b016 | ||
|
|
dd1e76e95a | ||
|
|
410e9f40cc | ||
|
|
1578c48440 | ||
|
|
7526d2b445 |
@@ -18,9 +18,10 @@ metadata {
|
|||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Smoke Detector" //attributes: smoke ("detected","clear","tested")
|
capability "Smoke Detector" //attributes: smoke ("detected","clear","tested")
|
||||||
capability "Temperature Measurement" //attributes: temperature
|
capability "Temperature Measurement" //attributes: temperature
|
||||||
attribute "tamper", "enum", ["detected", "clear"]
|
attribute "tamper", "enum", ["active", "inactive"]//capability "Tamper Alert"// <- yields "java.lang.RuntimeException: Metadata Error: Capability 'Tamper Alert' not found" error
|
||||||
attribute "heatAlarm", "enum", ["overheat detected", "clear", "rapid temperature rise", "underheat detected"]
|
attribute "heatAlarm", "enum", ["overheat", "inactive"]
|
||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x9C, 0x98, 0x7A", outClusters: "0x20, 0x8B"
|
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x9C, 0x98, 0x7A", outClusters: "0x20, 0x8B"
|
||||||
|
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x9C, 0x98, 0x7A"
|
||||||
}
|
}
|
||||||
simulator {
|
simulator {
|
||||||
//battery
|
//battery
|
||||||
@@ -50,7 +51,7 @@ metadata {
|
|||||||
input description: "Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings",
|
input description: "Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings",
|
||||||
title: "Advanced Configuration", displayDuringSetup: true, type: "paragraph", element: "paragraph"
|
title: "Advanced Configuration", displayDuringSetup: true, type: "paragraph", element: "paragraph"
|
||||||
input "smokeSensorSensitivity", "enum", title: "Smoke Sensor Sensitivity", options: ["High","Medium","Low"], defaultValue: "${smokeSensorSensitivity}", displayDuringSetup: true
|
input "smokeSensorSensitivity", "enum", title: "Smoke Sensor Sensitivity", options: ["High","Medium","Low"], defaultValue: "${smokeSensorSensitivity}", displayDuringSetup: true
|
||||||
input "zwaveNotificationStatus", "enum", title: "Notifications Status", options: ["disabled","casing opened","exceeding temperature threshold", "lack of Z-Wave range", "all notifications"],
|
input "zwaveNotificationStatus", "enum", title: "Notifications Status", options: ["disabled","casing opened","exceeding temperature threshold","all notifications"],
|
||||||
defaultValue: "${zwaveNotificationStatus}", displayDuringSetup: true
|
defaultValue: "${zwaveNotificationStatus}", displayDuringSetup: true
|
||||||
input "visualIndicatorNotificationStatus", "enum", title: "Visual Indicator Notifications Status",
|
input "visualIndicatorNotificationStatus", "enum", title: "Visual Indicator Notifications Status",
|
||||||
options: ["disabled","casing opened","exceeding temperature threshold", "lack of Z-Wave range", "all notifications"],
|
options: ["disabled","casing opened","exceeding temperature threshold", "lack of Z-Wave range", "all notifications"],
|
||||||
@@ -61,44 +62,47 @@ metadata {
|
|||||||
input "temperatureReportInterval", "enum", title: "Temperature Report Interval",
|
input "temperatureReportInterval", "enum", title: "Temperature Report Interval",
|
||||||
options: ["reports inactive", "5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${temperatureReportInterval}", displayDuringSetup: true
|
options: ["reports inactive", "5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${temperatureReportInterval}", displayDuringSetup: true
|
||||||
input "temperatureReportHysteresis", "number", title: "Temperature Report Hysteresis", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true
|
input "temperatureReportHysteresis", "number", title: "Temperature Report Hysteresis", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true
|
||||||
input "temperatureThreshold", "number", title: "Overheat Temperature Threshold", description: "Available settings: 0 or 2-100 C", range: "0..100", displayDuringSetup: true
|
input "temperatureThreshold", "number", title: "Overheat Temperature Threshold", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true
|
||||||
input "excessTemperatureSignalingInterval", "enum", title: "Excess Temperature Signaling Interval",
|
input "excessTemperatureSignalingInterval", "enum", title: "Excess Temperature Signaling Interval",
|
||||||
options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${excessTemperatureSignalingInterval}", displayDuringSetup: true
|
options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${excessTemperatureSignalingInterval}", displayDuringSetup: true
|
||||||
input "lackOfZwaveRangeIndicationInterval", "enum", title: "Lack of Z-Wave Range Indication Interval",
|
input "lackOfZwaveRangeIndicationInterval", "enum", title: "Lack of Z-Wave Range Indication Interval",
|
||||||
options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${lackOfZwaveRangeIndicationInterval}", displayDuringSetup: true
|
options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${lackOfZwaveRangeIndicationInterval}", displayDuringSetup: true
|
||||||
}
|
}
|
||||||
tiles (scale: 2){
|
tiles (scale: 2){
|
||||||
multiAttributeTile(name:"smoke", type: "lighting", width: 6, height: 4){
|
multiAttributeTile(name:"FGSD", type: "lighting", width: 6, height: 4){
|
||||||
tileAttribute ("device.smoke", key: "PRIMARY_CONTROL") {
|
tileAttribute ("device.smoke", key: "PRIMARY_CONTROL") {
|
||||||
attributeState("clear", label:"CLEAR", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
|
attributeState("clear", label:"CLEAR", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
|
||||||
attributeState("detected", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
|
attributeState("detected", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
|
||||||
attributeState("tested", label:"TEST", icon:"st.alarm.smoke.test", backgroundColor:"#e86d13")
|
attributeState("tested", label:"TEST", icon:"st.alarm.smoke.test", backgroundColor:"#e86d13")
|
||||||
attributeState("replacement required", label:"REPLACE", icon:"st.alarm.smoke.test", backgroundColor:"#FFFF66")
|
|
||||||
attributeState("unknown", label:"UNKNOWN", icon:"st.alarm.smoke.test", backgroundColor:"#ffffff")
|
attributeState("unknown", label:"UNKNOWN", icon:"st.alarm.smoke.test", backgroundColor:"#ffffff")
|
||||||
}
|
}
|
||||||
tileAttribute ("device.battery", key: "SECONDARY_CONTROL") {
|
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||||
attributeState "battery", label:'Battery: ${currentValue}%', unit:"%"
|
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||||
}
|
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||||
state "battery", label:'${currentValue}% battery', unit:"%"
|
state "battery", label:'${currentValue}% battery', unit:"%"
|
||||||
}
|
}
|
||||||
valueTile("temperature", "device.temperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||||
state "temperature", label:'${currentValue}°', unit:"C"
|
state "temperature", label:'${currentValue}°',
|
||||||
}
|
backgroundColors:[
|
||||||
valueTile("heatAlarm", "device.heatAlarm", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
[value: 31, color: "#153591"],
|
||||||
state "clear", label:'TEMPERATURE OK', backgroundColor:"#ffffff"
|
[value: 44, color: "#1e9cbb"],
|
||||||
state "overheat detected", label:'OVERHEAT DETECTED', backgroundColor:"#ffffff"
|
[value: 59, color: "#90d2a7"],
|
||||||
state "rapid temperature rise", label:'RAPID TEMP RISE', backgroundColor:"#ffffff"
|
[value: 74, color: "#44b621"],
|
||||||
state "underheat detected", label:'UNDERHEAT DETECTED', backgroundColor:"#ffffff"
|
[value: 84, color: "#f1d801"],
|
||||||
}
|
[value: 95, color: "#d04e00"],
|
||||||
valueTile("tamper", "device.tamper", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
[value: 96, color: "#bc2323"]
|
||||||
state "clear", label:'NO TAMPER', backgroundColor:"#ffffff"
|
]
|
||||||
state "detected", label:'TAMPER DETECTED', backgroundColor:"#ffffff"
|
}
|
||||||
|
valueTile("heatAlarm", "device.heatAlarm", inactiveLabel: false, width: 2, height: 2) {
|
||||||
|
state "inactive", label:'TEMPERATURE OK', backgroundColor:"#ffffff"
|
||||||
|
state "overheat", label:'OVERHEAT DETECTED', backgroundColor:"#bc2323"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "smoke"
|
main "FGSD"
|
||||||
details(["smoke","temperature"])
|
details(["FGSD","temperature", "battery", "heatAlarm"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +121,7 @@ def parse(String description) {
|
|||||||
"If you are unable to control it via SmartThings, you must remove it from your network and add it again.")
|
"If you are unable to control it via SmartThings, you must remove it from your network and add it again.")
|
||||||
} else if (description != "updated") {
|
} else if (description != "updated") {
|
||||||
log.debug "parse() >> zwave.parse(description)"
|
log.debug "parse() >> zwave.parse(description)"
|
||||||
def cmd = zwave.parse(description, [0x31: 5, 0x71: 3, 0x84: 1])
|
def cmd = zwave.parse(description, [0x31: 5, 0x5A: 1, 0x71: 3, 0x72: 2, 0x84: 1, 0x86: 1, 0x8B: 1, 0x9C: 1])
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
result = zwaveEvent(cmd)
|
result = zwaveEvent(cmd)
|
||||||
}
|
}
|
||||||
@@ -126,14 +130,40 @@ def parse(String description) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
|
//security
|
||||||
log.info "Executing zwaveEvent 86 (VersionV1): 12 (VersionReport) with cmd: $cmd"
|
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||||
def fw = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
|
setSecured()
|
||||||
updateDataValue("fw", fw)
|
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x5A: 1, 0x71: 3, 0x84: 1, 0x9C: 1])
|
||||||
def text = "$device.displayName: firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
|
if (encapsulatedCommand) {
|
||||||
createEvent(descriptionText: text, isStateChange: false)
|
log.debug "command: 98 (Security) 81(SecurityMessageEncapsulation) encapsulatedCommand: $encapsulatedCommand"
|
||||||
|
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, 0x86: 1, 0x8B: 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.versionv1.VersionReport cmd) {
|
||||||
|
log.info "Executing zwaveEvent 86 (VersionV1): 12 (VersionReport) with cmd: $cmd"
|
||||||
|
def version = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
|
||||||
|
updateDataValue("version", fw)
|
||||||
|
def text = "$device.displayName: firmware version: $version, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
|
||||||
|
createEvent(descriptionText: text, isStateChange: false)
|
||||||
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||||
def map = [ name: "battery", unit: "%" ]
|
def map = [ name: "battery", unit: "%" ]
|
||||||
@@ -161,18 +191,6 @@ def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationRejec
|
|||||||
createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
|
createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
|
||||||
setSecured()
|
|
||||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x71: 3, 0x84: 1])
|
|
||||||
if (encapsulatedCommand) {
|
|
||||||
log.debug "command: 98 (Security) 81(SecurityMessageEncapsulation) encapsulatedCommand: $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) {
|
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
|
||||||
log.info "Executing zwaveEvent 98 (SecurityV1): 03 (SecurityCommandsSupportedReport) with cmd: $cmd"
|
log.info "Executing zwaveEvent 98 (SecurityV1): 03 (SecurityCommandsSupportedReport) with cmd: $cmd"
|
||||||
setSecured()
|
setSecured()
|
||||||
@@ -199,18 +217,31 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
|
|||||||
if (cmd.notificationType == 7) {
|
if (cmd.notificationType == 7) {
|
||||||
switch (cmd.event) {
|
switch (cmd.event) {
|
||||||
case 0:
|
case 0:
|
||||||
result << createEvent(name: "tamper", value: "clear", displayed: false)
|
result << createEvent(name: "tamper", value: "inactive", displayed: false)
|
||||||
break
|
break
|
||||||
case 3:
|
case 3:
|
||||||
result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName casing was opened")
|
result << createEvent(name: "tamper", value: "active", descriptionText: "$device.displayName casing was opened")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if (cmd.notificationType == 1) { //Smoke Alarm (V2)
|
} else if (cmd.notificationType == 1) { //Smoke Alarm (V2)
|
||||||
log.debug "notificationv3.NotificationReport: for Smoke Alarm (V2)"
|
log.debug "notificationv3.NotificationReport: for Smoke Alarm (V2)"
|
||||||
result << smokeAlarmEvent(cmd.event)
|
result << smokeAlarmEvent(cmd.event)
|
||||||
} else if (cmd.notificationType == 4) { // Heat Alarm (V2)
|
} else if (cmd.notificationType == 4) { // Heat Alarm (V2)
|
||||||
log.debug "notificationv3.NotificationReport: for Heat Alarm (V2)"
|
log.debug "notificationv3.NotificationReport: for Heat Alarm (V2)"
|
||||||
result << heatAlarmEvent(cmd.event)
|
result << heatAlarmEvent(cmd.event)
|
||||||
|
} else if (cmd.notificationType == 8) {
|
||||||
|
if (cmd.event == 0x0A) {
|
||||||
|
def map = [:]
|
||||||
|
map.name = "battery"
|
||||||
|
map.value = 1
|
||||||
|
map.unit = "%"
|
||||||
|
map.displayed = true
|
||||||
|
result << createEvent(map)
|
||||||
|
}
|
||||||
|
} else if (cmd.notificationType == 9) {
|
||||||
|
if (cmd.event == 0x01) {
|
||||||
|
result << createEvent(descriptionText: "Warning: $device.displayName system hardware failure", isStateChange: true)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn "Need to handle this cmd.notificationType: ${cmd.notificationType}"
|
log.warn "Need to handle this cmd.notificationType: ${cmd.notificationType}"
|
||||||
result << createEvent(descriptionText: cmd.toString(), isStateChange: false)
|
result << createEvent(descriptionText: cmd.toString(), isStateChange: false)
|
||||||
@@ -221,7 +252,7 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
|
|||||||
def smokeAlarmEvent(value) {
|
def smokeAlarmEvent(value) {
|
||||||
log.debug "smokeAlarmEvent(value): $value"
|
log.debug "smokeAlarmEvent(value): $value"
|
||||||
def map = [name: "smoke"]
|
def map = [name: "smoke"]
|
||||||
if (value == 1 || value == 2) {
|
if (value == 2) {
|
||||||
map.value = "detected"
|
map.value = "detected"
|
||||||
map.descriptionText = "$device.displayName detected smoke"
|
map.descriptionText = "$device.displayName detected smoke"
|
||||||
} else if (value == 0) {
|
} else if (value == 0) {
|
||||||
@@ -230,9 +261,6 @@ def smokeAlarmEvent(value) {
|
|||||||
} else if (value == 3) {
|
} else if (value == 3) {
|
||||||
map.value = "tested"
|
map.value = "tested"
|
||||||
map.descriptionText = "$device.displayName smoke alarm test"
|
map.descriptionText = "$device.displayName smoke alarm test"
|
||||||
} else if (value == 4) {
|
|
||||||
map.value = "replacement required"
|
|
||||||
map.descriptionText = "$device.displayName replacement required"
|
|
||||||
} else {
|
} else {
|
||||||
map.value = "unknown"
|
map.value = "unknown"
|
||||||
map.descriptionText = "$device.displayName unknown event"
|
map.descriptionText = "$device.displayName unknown event"
|
||||||
@@ -243,18 +271,12 @@ def smokeAlarmEvent(value) {
|
|||||||
def heatAlarmEvent(value) {
|
def heatAlarmEvent(value) {
|
||||||
log.debug "heatAlarmEvent(value): $value"
|
log.debug "heatAlarmEvent(value): $value"
|
||||||
def map = [name: "heatAlarm"]
|
def map = [name: "heatAlarm"]
|
||||||
if (value == 1 || value == 2) {
|
if (value == 2) {
|
||||||
map.value = "overheat detected"
|
map.value = "overheat"
|
||||||
map.descriptionText = "$device.displayName overheat detected"
|
map.descriptionText = "$device.displayName overheat detected"
|
||||||
} else if (value == 0) {
|
} else if (value == 0) {
|
||||||
map.value = "clear"
|
map.value = "inactive"
|
||||||
map.descriptionText = "$device.displayName heat alarm cleared (no overheat)"
|
map.descriptionText = "$device.displayName heat alarm cleared (no overheat)"
|
||||||
} else if (value == 3 || value == 4) {
|
|
||||||
map.value = "rapid temperature rise"
|
|
||||||
map.descriptionText = "$device.displayName rapid temperature rise"
|
|
||||||
} else if (value == 5 || value == 6) {
|
|
||||||
map.value = "underheat detected"
|
|
||||||
map.descriptionText = "$device.displayName underheat detected"
|
|
||||||
} else {
|
} else {
|
||||||
map.value = "unknown"
|
map.value = "unknown"
|
||||||
map.descriptionText = "$device.displayName unknown event"
|
map.descriptionText = "$device.displayName unknown event"
|
||||||
@@ -274,12 +296,14 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
|
|||||||
//Only ask for battery if we haven't had a BatteryReport in a while
|
//Only ask for battery if we haven't had a BatteryReport in a while
|
||||||
if (!state.lastbatt || (new Date().time) - state.lastbatt > 24*60*60*1000) {
|
if (!state.lastbatt || (new Date().time) - state.lastbatt > 24*60*60*1000) {
|
||||||
log.debug("Device has been configured sending >> batteryGet()")
|
log.debug("Device has been configured sending >> batteryGet()")
|
||||||
cmds << zwave.securityV1.securityMessageEncapsulation().encapsulate(zwave.batteryV1.batteryGet()).format()
|
cmds << zwave.batteryV1.batteryGet()
|
||||||
cmds << "delay 1200"
|
|
||||||
}
|
}
|
||||||
|
cmds << zwave.sensorAlarmV1.sensorAlarmGet(sensorType: 0)
|
||||||
|
cmds << zwave.sensorAlarmV1.sensorAlarmGet(sensorType: 1)
|
||||||
|
cmds << zwave.sensorAlarmV1.sensorAlarmGet(sensorType: 4)
|
||||||
log.debug("Device has been configured sending >> wakeUpNoMoreInformation()")
|
log.debug("Device has been configured sending >> wakeUpNoMoreInformation()")
|
||||||
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation()
|
||||||
result << response(cmds) //tell device back to sleep
|
result << response(commands(cmds,500)) //tell device back to sleep
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -317,8 +341,26 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
|
|||||||
log.debug "After device is securely joined, send commands to update tiles"
|
log.debug "After device is securely joined, send commands to update tiles"
|
||||||
result << zwave.batteryV1.batteryGet()
|
result << zwave.batteryV1.batteryGet()
|
||||||
result << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01)
|
result << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01)
|
||||||
result << zwave.wakeUpV1.wakeUpNoMoreInformation()
|
//always send wakeUpNoMoreInformation as secure (at this point "secured" property is not set)
|
||||||
[[descriptionText:"${device.displayName} MSR report"], response(commands(result, 5000))]
|
[[descriptionText:"${device.displayName} MSR report"], response(commands(result, 500) << "delay 1000" << secure(zwave.wakeUpV1.wakeUpNoMoreInformation()))]
|
||||||
|
}
|
||||||
|
|
||||||
|
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 zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd) {
|
||||||
@@ -332,6 +374,37 @@ def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd)
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) {
|
||||||
|
def map = [:]
|
||||||
|
switch (cmd.sensorType) {
|
||||||
|
case 0:
|
||||||
|
map.name = "tamper"
|
||||||
|
map.value = cmd.sensorState == 0xFF ? "active" : "inactive"
|
||||||
|
map.descriptionText = cmd.sensorState == 0xFF ? "$device.displayName casing was opened" : ""
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
map.name = "smoke"
|
||||||
|
map.value = cmd.sensorState == 0xFF ? "detected" : "clear"
|
||||||
|
map.descriptionText = cmd.sensorState == 0xFF ? "$device.displayName detected smoke" : "$device.displayName is clear (no smoke)"
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
map.name = "heatAlarm"
|
||||||
|
map.value = cmd.sensorState == 0xFF ? "overheat" : "inactive"
|
||||||
|
map.descriptionText = cmd.sensorState == 0xFF ? "$device.displayName overheat detected" : "$device.displayName heat alarm cleared (no overheat)"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
createEvent(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.timeparametersv1.TimeParametersGet cmd) {
|
||||||
|
log.info "Executing zwaveEvent 8B (TimeParametersV1) : 02 (TimeParametersGet) with cmd: $cmd"
|
||||||
|
def nowCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||||
|
//Time Parameters are requested by an un-encapsulated frame
|
||||||
|
response(zwave.timeParametersV1.timeParametersReport(year: nowCal.get(Calendar.YEAR), month: (nowCal.get(Calendar.MONTH) + 1), day: nowCal.get(Calendar.DAY_OF_MONTH),
|
||||||
|
hourUtc: nowCal.get(Calendar.HOUR_OF_DAY), minuteUtc: nowCal.get(Calendar.MINUTE), secondUtc: nowCal.get(Calendar.SECOND)).format())
|
||||||
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||||
log.warn "General zwaveEvent cmd: ${cmd}"
|
log.warn "General zwaveEvent cmd: ${cmd}"
|
||||||
createEvent(descriptionText: cmd.toString(), isStateChange: false)
|
createEvent(descriptionText: cmd.toString(), isStateChange: false)
|
||||||
@@ -360,7 +433,7 @@ def configure() {
|
|||||||
}
|
}
|
||||||
//3. Z-Wave notification status: 0-all disabled (default), 1-casing open enabled, 2-exceeding temp enable
|
//3. Z-Wave notification status: 0-all disabled (default), 1-casing open enabled, 2-exceeding temp enable
|
||||||
if (zwaveNotificationStatus && zwaveNotificationStatus != "null"){
|
if (zwaveNotificationStatus && zwaveNotificationStatus != "null"){
|
||||||
request += zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, scaledConfigurationValue: notificationOptionValueMap[zwaveNotificationStatus] ?: 0)
|
request += zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, scaledConfigurationValue: zwaveNotificationOptionValueMap[zwaveNotificationStatus] ?: 0)
|
||||||
}
|
}
|
||||||
//4. Visual indicator notification status: 0-all disabled (default), 1-casing open enabled, 2-exceeding temp enable, 4-lack of range notification
|
//4. Visual indicator notification status: 0-all disabled (default), 1-casing open enabled, 2-exceeding temp enable, 4-lack of range notification
|
||||||
if (visualIndicatorNotificationStatus && visualIndicatorNotificationStatus != "null") {
|
if (visualIndicatorNotificationStatus && visualIndicatorNotificationStatus != "null") {
|
||||||
@@ -402,7 +475,16 @@ def configure() {
|
|||||||
//12. get temperature reading from device
|
//12. get temperature reading from device
|
||||||
request += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01)
|
request += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01)
|
||||||
|
|
||||||
commands(request) + ["delay 10000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()]
|
//13. set association group
|
||||||
|
request += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId])
|
||||||
|
|
||||||
|
//14. get version
|
||||||
|
request += zwave.versionV1.versionGet()
|
||||||
|
|
||||||
|
//15. get serial number
|
||||||
|
request += zwave.manufacturerSpecificV2.deviceSpecificGet()
|
||||||
|
|
||||||
|
commands(request) + ["delay 1000", command(zwave.wakeUpV1.wakeUpNoMoreInformation())]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,24 +500,42 @@ private def getTimeOptionValueMap() { [
|
|||||||
"reports inactive" : 0,
|
"reports inactive" : 0,
|
||||||
]}
|
]}
|
||||||
|
|
||||||
|
private def getZwaveNotificationOptionValueMap() { [
|
||||||
|
"disabled" : 0,
|
||||||
|
"casing opened" : 1,
|
||||||
|
"exceeding temperature threshold" : 2,
|
||||||
|
"all notifications" : 3
|
||||||
|
]}
|
||||||
|
|
||||||
private def getNotificationOptionValueMap() { [
|
private def getNotificationOptionValueMap() { [
|
||||||
"disabled" : 0,
|
"disabled" : 0,
|
||||||
"casing opened" : 1,
|
"casing opened" : 1,
|
||||||
"exceeding temperature threshold" : 2,
|
"exceeding temperature threshold" : 2,
|
||||||
"lack of Z-Wave range" : 4,
|
"lack of Z-Wave range" : 4,
|
||||||
"all notifications" : 7,
|
"all notifications" : 7
|
||||||
]}
|
]}
|
||||||
|
|
||||||
private command(physicalgraph.zwave.Command cmd) {
|
private command(physicalgraph.zwave.Command cmd) {
|
||||||
if (isSecured()) {
|
def secureClasses = [0x20, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
|
||||||
|
|
||||||
|
if (isSecured() && secureClasses.find{ it == cmd.commandClassId }) {
|
||||||
log.info "Sending secured command: ${cmd}"
|
log.info "Sending secured command: ${cmd}"
|
||||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
secure(cmd)
|
||||||
} else {
|
} else {
|
||||||
log.info "Sending unsecured command: ${cmd}"
|
log.info "Sending unsecured command: ${cmd}"
|
||||||
cmd.format()
|
crc16(cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 commands(commands, delay=200) {
|
private commands(commands, delay=200) {
|
||||||
log.info "inside commands: ${commands}"
|
log.info "inside commands: ${commands}"
|
||||||
delayBetween(commands.collect{ command(it) }, delay)
|
delayBetween(commands.collect{ command(it) }, delay)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
# Korean (ko)
|
# Korean (ko)
|
||||||
# Device Preferences
|
# Device Preferences
|
||||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||||
|
'''Outlet'''.ko=플러그
|
||||||
# Events descriptionText
|
# Events descriptionText
|
||||||
'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다.
|
'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다.
|
||||||
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다.
|
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다.
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
* 1. 20160117 TW - Update/Edit to support i18n translations
|
* 1. 20160117 TW - Update/Edit to support i18n translations
|
||||||
===============================================================================
|
===============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
// Automatically generated. Make future change here.
|
// Automatically generated. Make future change here.
|
||||||
definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
|
||||||
@@ -104,8 +103,8 @@ def parse(String description) {
|
|||||||
log.info "$device updates: ${finalResult.value}"
|
log.info "$device updates: ${finalResult.value}"
|
||||||
}
|
}
|
||||||
else if (finalResult.type == "power") {
|
else if (finalResult.type == "power") {
|
||||||
def value = (finalResult.value as Integer)/10
|
def powerValue = (finalResult.value as Integer)/10
|
||||||
createEvent(name: "power", value: value, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true )
|
sendEvent(name: "power", value: powerValue, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true )
|
||||||
/*
|
/*
|
||||||
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
|
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
|
||||||
power level is an integer. The exact power level with correct units needs to be handled in the device type
|
power level is an integer. The exact power level with correct units needs to be handled in the device type
|
||||||
@@ -113,10 +112,8 @@ def parse(String description) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( finalResult.value == "on" )
|
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
||||||
createEvent(name: finalResult.type, value: finalResult.value, descriptionText: '{{ device.displayName }} is On', translatable: true)
|
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
|
||||||
else
|
|
||||||
createEvent(name: finalResult.type, value: finalResult.value, descriptionText: '{{ device.displayName }} is Off', translatable: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -30,11 +30,12 @@
|
|||||||
'''Degrees'''.ko=온도
|
'''Degrees'''.ko=온도
|
||||||
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
|
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
|
||||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||||
|
'''Water Leak Sensor'''.ko=누수센서
|
||||||
# Events descriptionText
|
# Events descriptionText
|
||||||
'''{{ device.displayName }} is dry'''.ko={{ device.displayName }}가 건조
|
'''{{ device.displayName }} is dry'''.ko={{ device.displayName }}가 건조
|
||||||
'''{{ device.displayName }} is wet'''.ko={{ device.displayName }}누수
|
'''{{ device.displayName }} is wet'''.ko={{ device.displayName }}누수
|
||||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
|
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
|
||||||
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
||||||
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
|
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
|
||||||
'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}입니다.
|
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ metadata {
|
|||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
capability "Water Sensor"
|
capability "Water Sensor"
|
||||||
|
|
||||||
command "enrollResponse"
|
command "enrollResponse"
|
||||||
|
|
||||||
|
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315"
|
||||||
@@ -66,7 +67,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||||
state "temperature", label:'${currentValue}°',
|
state "temperature", label:'${currentValue}°',
|
||||||
backgroundColors:[
|
backgroundColors:[
|
||||||
[value: 31, color: "#153591"],
|
[value: 31, color: "#153591"],
|
||||||
[value: 44, color: "#1e9cbb"],
|
[value: 44, color: "#1e9cbb"],
|
||||||
@@ -102,29 +103,29 @@ def parse(String description) {
|
|||||||
else if (description?.startsWith('temperature: ')) {
|
else if (description?.startsWith('temperature: ')) {
|
||||||
map = parseCustomMessage(description)
|
map = parseCustomMessage(description)
|
||||||
}
|
}
|
||||||
else if (description?.startsWith('zone status')) {
|
else if (description?.startsWith('zone status')) {
|
||||||
map = parseIasMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
if (description?.startsWith('enroll request')) {
|
if (description?.startsWith('enroll request')) {
|
||||||
List cmds = enrollResponse()
|
List cmds = enrollResponse()
|
||||||
log.debug "enroll response: ${cmds}"
|
log.debug "enroll response: ${cmds}"
|
||||||
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
private Map parseCatchAllMessage(String description) {
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
def cluster = zigbee.parse(description)
|
def cluster = zigbee.parse(description)
|
||||||
if (shouldProcessMessage(cluster)) {
|
if (shouldProcessMessage(cluster)) {
|
||||||
switch(cluster.clusterId) {
|
switch(cluster.clusterId) {
|
||||||
case 0x0001:
|
case 0x0001:
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
resultMap = getBatteryResult(cluster.data.last())
|
||||||
break
|
break
|
||||||
|
|
||||||
case 0x0402:
|
case 0x0402:
|
||||||
// temp is last 2 data values. reverse to swap endian
|
// temp is last 2 data values. reverse to swap endian
|
||||||
@@ -139,13 +140,13 @@ private Map parseCatchAllMessage(String description) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
private boolean shouldProcessMessage(cluster) {
|
||||||
// 0x0B is default response indicating message got through
|
// 0x0B is default response indicating message got through
|
||||||
// 0x07 is bind message
|
// 0x07 is bind message
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
||||||
cluster.command == 0x0B ||
|
cluster.command == 0x0B ||
|
||||||
cluster.command == 0x07 ||
|
cluster.command == 0x07 ||
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
||||||
return !ignoredMessage
|
return !ignoredMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseReportAttributeMessage(String description) {
|
private Map parseReportAttributeMessage(String description) {
|
||||||
@@ -177,42 +178,42 @@ private Map parseCustomMessage(String description) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map parseIasMessage(String description) {
|
private Map parseIasMessage(String description) {
|
||||||
List parsedMsg = description.split(' ')
|
List parsedMsg = description.split(' ')
|
||||||
String msgCode = parsedMsg[2]
|
String msgCode = parsedMsg[2]
|
||||||
|
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
switch(msgCode) {
|
switch(msgCode) {
|
||||||
case '0x0020': // Closed/No Motion/Dry
|
case '0x0020': // Closed/No Motion/Dry
|
||||||
resultMap = getMoistureResult('dry')
|
resultMap = getMoistureResult('dry')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0021': // Open/Motion/Wet
|
case '0x0021': // Open/Motion/Wet
|
||||||
resultMap = getMoistureResult('wet')
|
resultMap = getMoistureResult('wet')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0022': // Tamper Alarm
|
case '0x0022': // Tamper Alarm
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0023': // Battery Alarm
|
case '0x0023': // Battery Alarm
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0024': // Supervision Report
|
case '0x0024': // Supervision Report
|
||||||
log.debug 'dry with tamper alarm'
|
log.debug 'dry with tamper alarm'
|
||||||
resultMap = getMoistureResult('dry')
|
resultMap = getMoistureResult('dry')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0025': // Restore Report
|
case '0x0025': // Restore Report
|
||||||
log.debug 'water with tamper alarm'
|
log.debug 'water with tamper alarm'
|
||||||
resultMap = getMoistureResult('wet')
|
resultMap = getMoistureResult('wet')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0026': // Trouble/Failure
|
case '0x0026': // Trouble/Failure
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0028': // Test Mode
|
case '0x0028': // Test Mode
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
def getTemperature(value) {
|
||||||
@@ -256,8 +257,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def pct = batteryMap[volts]
|
def pct = batteryMap[volts]
|
||||||
if (pct != null) {
|
if (pct != null) {
|
||||||
result.value = pct
|
result.value = pct
|
||||||
def value = pct
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -265,13 +265,14 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
result.value = Math.min(100, (int) pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getTemperatureResult(value) {
|
private Map getTemperatureResult(value) {
|
||||||
log.debug 'TEMP'
|
log.debug 'TEMP'
|
||||||
if (tempOffset) {
|
if (tempOffset) {
|
||||||
@@ -281,9 +282,9 @@ private Map getTemperatureResult(value) {
|
|||||||
}
|
}
|
||||||
def descriptionText
|
def descriptionText
|
||||||
if ( temperatureScale == 'C' )
|
if ( temperatureScale == 'C' )
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°C'
|
descriptionText = '{{ device.displayName }} was {{ value }}°C'
|
||||||
else
|
else
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°F'
|
descriptionText = '{{ device.displayName }} was {{ value }}°F'
|
||||||
|
|
||||||
return [
|
return [
|
||||||
name: 'temperature',
|
name: 'temperature',
|
||||||
@@ -311,7 +312,7 @@ private Map getMoistureResult(value) {
|
|||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "Refreshing Temperature and Battery"
|
log.debug "Refreshing Temperature and Battery"
|
||||||
def refreshCmds = [
|
def refreshCmds = [
|
||||||
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
|
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
|
||||||
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
|
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -327,26 +328,26 @@ def configure() {
|
|||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
|
||||||
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
|
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
||||||
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
|
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||||
]
|
]
|
||||||
return configCmds + refresh() // send refresh cmds as part of config
|
return configCmds + refresh() // send refresh cmds as part of config
|
||||||
}
|
}
|
||||||
|
|
||||||
def enrollResponse() {
|
def enrollResponse() {
|
||||||
log.debug "Sending enroll response"
|
log.debug "Sending enroll response"
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
[
|
[
|
||||||
//Resending the CIE in case the enroll request is sent before CIE is written
|
//Resending the CIE in case the enroll request is sent before CIE is written
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
//Enroll Response
|
//Enroll Response
|
||||||
"raw 0x500 {01 23 00 00 00}",
|
"raw 0x500 {01 23 00 00 00}",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
|
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private getEndpointId() {
|
private getEndpointId() {
|
||||||
@@ -358,19 +359,19 @@ private hex(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String swapEndianHex(String hex) {
|
private String swapEndianHex(String hex) {
|
||||||
reverseArray(hex.decodeHex()).encodeHex()
|
reverseArray(hex.decodeHex()).encodeHex()
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] reverseArray(byte[] array) {
|
private byte[] reverseArray(byte[] array) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = array.length - 1;
|
int j = array.length - 1;
|
||||||
byte tmp;
|
byte tmp;
|
||||||
while (j > i) {
|
while (j > i) {
|
||||||
tmp = array[j];
|
tmp = array[j];
|
||||||
array[j] = array[i];
|
array[j] = array[i];
|
||||||
array[i] = tmp;
|
array[i] = tmp;
|
||||||
j--;
|
j--;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
@@ -29,11 +29,12 @@
|
|||||||
'''Degrees'''.ko=온도
|
'''Degrees'''.ko=온도
|
||||||
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
|
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
|
||||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||||
|
'''Motion Sensor'''.ko=모션 센서
|
||||||
# Events descriptionText
|
# Events descriptionText
|
||||||
'''{{ device.displayName }} detected motion'''.ko={{ device.displayName }} 가 움직임을 감지하였습니다.
|
'''{{ device.displayName }} detected motion'''.ko={{ device.displayName }} 가 움직임을 감지하였습니다.
|
||||||
'''{{ device.displayName }} motion has stopped'''.ko={{ device.displayName }}움직임이 중단되었습니다
|
'''{{ device.displayName }} motion has stopped'''.ko={{ device.displayName }}움직임이 중단되었습니다
|
||||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
|
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
|
||||||
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
||||||
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
|
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
|
||||||
'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}입니다.
|
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
|
|||||||
@@ -29,17 +29,18 @@ metadata {
|
|||||||
capability "Motion Sensor"
|
capability "Motion Sensor"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
|
|
||||||
command "enrollResponse"
|
command "enrollResponse"
|
||||||
|
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325-S", deviceJoinName: "Motion Sensor"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325-S", deviceJoinName: "Motion Sensor"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326"
|
||||||
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326-L", deviceJoinName: "Iris Motion Sensor"
|
||||||
|
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
@@ -106,55 +107,55 @@ def parse(String description) {
|
|||||||
else if (description?.startsWith('temperature: ')) {
|
else if (description?.startsWith('temperature: ')) {
|
||||||
map = parseCustomMessage(description)
|
map = parseCustomMessage(description)
|
||||||
}
|
}
|
||||||
else if (description?.startsWith('zone status')) {
|
else if (description?.startsWith('zone status')) {
|
||||||
map = parseIasMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
if (description?.startsWith('enroll request')) {
|
if (description?.startsWith('enroll request')) {
|
||||||
List cmds = enrollResponse()
|
List cmds = enrollResponse()
|
||||||
log.debug "enroll response: ${cmds}"
|
log.debug "enroll response: ${cmds}"
|
||||||
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
private Map parseCatchAllMessage(String description) {
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
def cluster = zigbee.parse(description)
|
def cluster = zigbee.parse(description)
|
||||||
if (shouldProcessMessage(cluster)) {
|
if (shouldProcessMessage(cluster)) {
|
||||||
switch(cluster.clusterId) {
|
switch(cluster.clusterId) {
|
||||||
case 0x0001:
|
case 0x0001:
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
resultMap = getBatteryResult(cluster.data.last())
|
||||||
break
|
break
|
||||||
|
|
||||||
case 0x0402:
|
case 0x0402:
|
||||||
// temp is last 2 data values. reverse to swap endian
|
// temp is last 2 data values. reverse to swap endian
|
||||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
||||||
def value = getTemperature(temp)
|
def value = getTemperature(temp)
|
||||||
resultMap = getTemperatureResult(value)
|
resultMap = getTemperatureResult(value)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 0x0406:
|
case 0x0406:
|
||||||
log.debug 'motion'
|
log.debug 'motion'
|
||||||
resultMap.name = 'motion'
|
resultMap.name = 'motion'
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
private boolean shouldProcessMessage(cluster) {
|
||||||
// 0x0B is default response indicating message got through
|
// 0x0B is default response indicating message got through
|
||||||
// 0x07 is bind message
|
// 0x07 is bind message
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
||||||
cluster.command == 0x0B ||
|
cluster.command == 0x0B ||
|
||||||
cluster.command == 0x07 ||
|
cluster.command == 0x07 ||
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
||||||
return !ignoredMessage
|
return !ignoredMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseReportAttributeMessage(String description) {
|
private Map parseReportAttributeMessage(String description) {
|
||||||
@@ -172,10 +173,10 @@ private Map parseReportAttributeMessage(String description) {
|
|||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
||||||
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
}
|
}
|
||||||
else if (descMap.cluster == "0406" && descMap.attrId == "0000") {
|
else if (descMap.cluster == "0406" && descMap.attrId == "0000") {
|
||||||
def value = descMap.value.endsWith("01") ? "active" : "inactive"
|
def value = descMap.value.endsWith("01") ? "active" : "inactive"
|
||||||
resultMap = getMotionResult(value)
|
resultMap = getMotionResult(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
@@ -190,44 +191,44 @@ private Map parseCustomMessage(String description) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map parseIasMessage(String description) {
|
private Map parseIasMessage(String description) {
|
||||||
List parsedMsg = description.split(' ')
|
List parsedMsg = description.split(' ')
|
||||||
String msgCode = parsedMsg[2]
|
String msgCode = parsedMsg[2]
|
||||||
|
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
switch(msgCode) {
|
switch(msgCode) {
|
||||||
case '0x0020': // Closed/No Motion/Dry
|
case '0x0020': // Closed/No Motion/Dry
|
||||||
resultMap = getMotionResult('inactive')
|
resultMap = getMotionResult('inactive')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0021': // Open/Motion/Wet
|
case '0x0021': // Open/Motion/Wet
|
||||||
resultMap = getMotionResult('active')
|
resultMap = getMotionResult('active')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0022': // Tamper Alarm
|
case '0x0022': // Tamper Alarm
|
||||||
log.debug 'motion with tamper alarm'
|
log.debug 'motion with tamper alarm'
|
||||||
resultMap = getMotionResult('active')
|
resultMap = getMotionResult('active')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0023': // Battery Alarm
|
case '0x0023': // Battery Alarm
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0024': // Supervision Report
|
case '0x0024': // Supervision Report
|
||||||
log.debug 'no motion with tamper alarm'
|
log.debug 'no motion with tamper alarm'
|
||||||
resultMap = getMotionResult('inactive')
|
resultMap = getMotionResult('inactive')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0025': // Restore Report
|
case '0x0025': // Restore Report
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0026': // Trouble/Failure
|
case '0x0026': // Trouble/Failure
|
||||||
log.debug 'motion with failure alarm'
|
log.debug 'motion with failure alarm'
|
||||||
resultMap = getMotionResult('active')
|
resultMap = getMotionResult('active')
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0028': // Test Mode
|
case '0x0028': // Test Mode
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
def getTemperature(value) {
|
||||||
@@ -272,7 +273,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
if (pct != null) {
|
if (pct != null) {
|
||||||
result.value = pct
|
result.value = pct
|
||||||
def value = pct
|
def value = pct
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -280,7 +281,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
result.value = Math.min(100, (int) pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,7 +313,6 @@ private Map getTemperatureResult(value) {
|
|||||||
private Map getMotionResult(value) {
|
private Map getMotionResult(value) {
|
||||||
log.debug 'motion'
|
log.debug 'motion'
|
||||||
String descriptionText = value == 'active' ? "{{ device.displayName }} detected motion" : "{{ device.displayName }} motion has stopped"
|
String descriptionText = value == 'active' ? "{{ device.displayName }} detected motion" : "{{ device.displayName }} motion has stopped"
|
||||||
//String descriptionText = '{{ device.displayName }} is {{ value | translate }}'
|
|
||||||
return [
|
return [
|
||||||
name: 'motion',
|
name: 'motion',
|
||||||
value: value,
|
value: value,
|
||||||
@@ -322,7 +322,7 @@ private Map getMotionResult(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "refresh executed"
|
log.debug "refresh called"
|
||||||
def refreshCmds = [
|
def refreshCmds = [
|
||||||
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
|
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
|
||||||
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
|
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
|
||||||
@@ -372,19 +372,19 @@ private hex(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String swapEndianHex(String hex) {
|
private String swapEndianHex(String hex) {
|
||||||
reverseArray(hex.decodeHex()).encodeHex()
|
reverseArray(hex.decodeHex()).encodeHex()
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] reverseArray(byte[] array) {
|
private byte[] reverseArray(byte[] array) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = array.length - 1;
|
int j = array.length - 1;
|
||||||
byte tmp;
|
byte tmp;
|
||||||
while (j > i) {
|
while (j > i) {
|
||||||
tmp = array[j];
|
tmp = array[j];
|
||||||
array[j] = array[i];
|
array[j] = array[i];
|
||||||
array[i] = tmp;
|
array[i] = tmp;
|
||||||
j--;
|
j--;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
'''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
|
'''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
|
||||||
'''Tap to set'''.ko=눌러서 설정
|
'''Tap to set'''.ko=눌러서 설정
|
||||||
'''Give your device a name'''.ko=기기 이름 바꾸기
|
'''Give your device a name'''.ko=기기 이름 바꾸기
|
||||||
|
'''Multipurpose Sensor'''.ko=멀티 센서
|
||||||
# Events descriptionText
|
# Events descriptionText
|
||||||
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}열림을 감지하였습니다.
|
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}열림을 감지하였습니다.
|
||||||
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}닫혔습니다.
|
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}닫혔습니다.
|
||||||
@@ -39,8 +40,6 @@
|
|||||||
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}비활성화되었습니다.
|
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}비활성화되었습니다.
|
||||||
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
|
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
|
||||||
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
|
||||||
'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}입니다.
|
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
|
||||||
'''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중
|
'''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중
|
||||||
'''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중
|
'''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중
|
||||||
'''{{ device.displayName }} status was closed'''.ko={{ device.displayName }}은(는) 닫힌 상태입니다
|
|
||||||
'''{{ device.displayName }} status was opened'''.ko={{ device.displayName }}은(는) 열린 상태입니다
|
|
||||||
|
|||||||
@@ -24,12 +24,13 @@
|
|||||||
===============================================================================
|
===============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Three Axis"
|
|
||||||
|
capability "Three Axis"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Contact Sensor"
|
capability "Contact Sensor"
|
||||||
capability "Acceleration Sensor"
|
capability "Acceleration Sensor"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
@@ -38,13 +39,13 @@
|
|||||||
command "enrollResponse"
|
command "enrollResponse"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor"
|
||||||
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor"
|
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor"
|
||||||
|
|
||||||
attribute "status", "string"
|
attribute "status", "string"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
status "open": "zone report :: type: 19 value: 0031"
|
status "open": "zone report :: type: 19 value: 0031"
|
||||||
status "closed": "zone report :: type: 19 value: 0030"
|
status "closed": "zone report :: type: 19 value: 0030"
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
status "x,y,z: 0,1000,0": "x: 0, y: 1000, z: 0"
|
status "x,y,z: 0,1000,0": "x: 0, y: 1000, z: 0"
|
||||||
status "x,y,z: 0,0,1000": "x: 0, y: 0, z: 1000"
|
status "x,y,z: 0,0,1000": "x: 0, y: 0, z: 1000"
|
||||||
}
|
}
|
||||||
preferences {
|
preferences {
|
||||||
section {
|
section {
|
||||||
image(name: 'educationalcontent', multiple: true, images: [
|
image(name: 'educationalcontent', multiple: true, images: [
|
||||||
"http://cdn.device-gse.smartthings.com/Multi/Multi1.jpg",
|
"http://cdn.device-gse.smartthings.com/Multi/Multi1.jpg",
|
||||||
@@ -75,9 +76,9 @@
|
|||||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", translatable: true, description: "Tap to set", options: ["Yes","No"], defaultValue: "No", required: false, displayDuringSetup: false)
|
input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", description: "Tap to set", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
|
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
|
||||||
@@ -109,19 +110,16 @@
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
valueTile("3axis", "device.threeAxis", decoration: "flat", wordWrap: false, width: 2, height: 2) {
|
|
||||||
state("threeAxis", label:'${currentValue}', unit:"", backgroundColor:"#ffffff")
|
|
||||||
}
|
|
||||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||||
state "battery", label:'${currentValue}% battery', unit:""
|
state "battery", label:'${currentValue}% battery', unit:""
|
||||||
}
|
}
|
||||||
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
main(["status", "acceleration", "temperature"])
|
main(["status", "acceleration", "temperature"])
|
||||||
details(["status", "acceleration", "temperature", "3axis", "battery", "refresh"])
|
details(["status", "acceleration", "temperature", "battery", "refresh"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,61 +128,61 @@ def parse(String description) {
|
|||||||
if (description?.startsWith('catchall:')) {
|
if (description?.startsWith('catchall:')) {
|
||||||
map = parseCatchAllMessage(description)
|
map = parseCatchAllMessage(description)
|
||||||
}
|
}
|
||||||
else if (description?.startsWith('temperature: ')) {
|
else if (description?.startsWith('temperature: ')) {
|
||||||
map = parseCustomMessage(description)
|
map = parseCustomMessage(description)
|
||||||
}
|
}
|
||||||
else if (description?.startsWith('zone status')) {
|
else if (description?.startsWith('zone status')) {
|
||||||
map = parseIasMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
if (description?.startsWith('enroll request')) {
|
if (description?.startsWith('enroll request')) {
|
||||||
List cmds = enrollResponse()
|
List cmds = enrollResponse()
|
||||||
log.debug "enroll response: ${cmds}"
|
log.debug "enroll response: ${cmds}"
|
||||||
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
||||||
}
|
}
|
||||||
else if (description?.startsWith('read attr -')) {
|
else if (description?.startsWith('read attr -')) {
|
||||||
result = parseReportAttributeMessage(description).each { createEvent(it) }
|
result = parseReportAttributeMessage(description).each { createEvent(it) }
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
private Map parseCatchAllMessage(String description) {
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
def cluster = zigbee.parse(description)
|
def cluster = zigbee.parse(description)
|
||||||
log.debug cluster
|
log.debug cluster
|
||||||
if (shouldProcessMessage(cluster)) {
|
if (shouldProcessMessage(cluster)) {
|
||||||
switch(cluster.clusterId) {
|
switch(cluster.clusterId) {
|
||||||
case 0x0001:
|
case 0x0001:
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
resultMap = getBatteryResult(cluster.data.last())
|
||||||
break
|
break
|
||||||
|
|
||||||
case 0xFC02:
|
case 0xFC02:
|
||||||
log.debug 'ACCELERATION'
|
log.debug 'ACCELERATION'
|
||||||
break
|
break
|
||||||
|
|
||||||
case 0x0402:
|
case 0x0402:
|
||||||
log.debug 'TEMP'
|
log.debug 'TEMP'
|
||||||
// temp is last 2 data values. reverse to swap endian
|
// temp is last 2 data values. reverse to swap endian
|
||||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
||||||
def value = getTemperature(temp)
|
def value = getTemperature(temp)
|
||||||
resultMap = getTemperatureResult(value)
|
resultMap = getTemperatureResult(value)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
private boolean shouldProcessMessage(cluster) {
|
||||||
// 0x0B is default response indicating message got through
|
// 0x0B is default response indicating message got through
|
||||||
// 0x07 is bind message
|
// 0x07 is bind message
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
||||||
cluster.command == 0x0B ||
|
cluster.command == 0x0B ||
|
||||||
cluster.command == 0x07 ||
|
cluster.command == 0x07 ||
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
||||||
return !ignoredMessage
|
return !ignoredMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
private List parseReportAttributeMessage(String description) {
|
private List parseReportAttributeMessage(String description) {
|
||||||
@@ -211,10 +209,12 @@ private List parseReportAttributeMessage(String description) {
|
|||||||
result << parseAxis(threeAxisAttributes)
|
result << parseAxis(threeAxisAttributes)
|
||||||
descMap.value = descMap.value[-2..-1]
|
descMap.value = descMap.value[-2..-1]
|
||||||
}
|
}
|
||||||
result << getAccelerationResult(descMap.value)
|
result << getAccelerationResult(descMap.value)
|
||||||
}
|
}
|
||||||
else if (descMap.cluster == "FC02" && descMap.attrId == "0012" && descMap.value.size() == 24) {
|
else if (descMap.cluster == "FC02" && descMap.attrId == "0012" && descMap.value.size() == 24) {
|
||||||
result << parseAxis(descMap.value)
|
// The size is checked to ensure the attribute report contains X, Y and Z values
|
||||||
|
// If all three axis are not included then the attribute report is ignored
|
||||||
|
result << parseAxis(descMap.value)
|
||||||
}
|
}
|
||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
||||||
result << getBatteryResult(Integer.parseInt(descMap.value, 16))
|
result << getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
@@ -238,43 +238,43 @@ private Map parseIasMessage(String description) {
|
|||||||
|
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
switch(msgCode) {
|
switch(msgCode) {
|
||||||
case '0x0020': // Closed/No Motion/Dry
|
case '0x0020': // Closed/No Motion/Dry
|
||||||
if (garageSensor != "Yes"){
|
if (garageSensor != "Yes"){
|
||||||
resultMap = getContactResult('closed')
|
resultMap = getContactResult('closed')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0021': // Open/Motion/Wet
|
case '0x0021': // Open/Motion/Wet
|
||||||
if (garageSensor != "Yes"){
|
if (garageSensor != "Yes"){
|
||||||
resultMap = getContactResult('open')
|
resultMap = getContactResult('open')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0022': // Tamper Alarm
|
case '0x0022': // Tamper Alarm
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0023': // Battery Alarm
|
case '0x0023': // Battery Alarm
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0024': // Supervision Report
|
case '0x0024': // Supervision Report
|
||||||
if (garageSensor != "Yes"){
|
if (garageSensor != "Yes"){
|
||||||
resultMap = getContactResult('closed')
|
resultMap = getContactResult('closed')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0025': // Restore Report
|
case '0x0025': // Restore Report
|
||||||
if (garageSensor != "Yes"){
|
if (garageSensor != "Yes"){
|
||||||
resultMap = getContactResult('open')
|
resultMap = getContactResult('open')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0026': // Trouble/Failure
|
case '0x0026': // Trouble/Failure
|
||||||
break
|
break
|
||||||
|
|
||||||
case '0x0028': // Test Mode
|
case '0x0028': // Test Mode
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
@@ -311,7 +311,6 @@ def getTemperature(value) {
|
|||||||
|
|
||||||
private Map getBatteryResult(rawValue) {
|
private Map getBatteryResult(rawValue) {
|
||||||
log.debug "Battery rawValue = ${rawValue}"
|
log.debug "Battery rawValue = ${rawValue}"
|
||||||
def linkText = getLinkText(device)
|
|
||||||
|
|
||||||
def result = [
|
def result = [
|
||||||
name: 'battery',
|
name: 'battery',
|
||||||
@@ -341,8 +340,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def pct = batteryMap[volts]
|
def pct = batteryMap[volts]
|
||||||
if (pct != null) {
|
if (pct != null) {
|
||||||
result.value = pct
|
result.value = pct
|
||||||
def value = pct
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -350,7 +348,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
result.value = Math.min(100, (int) pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,52 +357,32 @@ private Map getBatteryResult(rawValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map getTemperatureResult(value) {
|
private Map getTemperatureResult(value) {
|
||||||
log.debug 'TEMP'
|
log.debug "Temperature"
|
||||||
def name = "temperature"
|
|
||||||
if (tempOffset) {
|
if (tempOffset) {
|
||||||
def offset = tempOffset as int
|
def offset = tempOffset as int
|
||||||
def v = value as int
|
def v = value as int
|
||||||
value = v + offset
|
value = v + offset
|
||||||
}
|
}
|
||||||
|
def descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C':
|
||||||
def descriptionText
|
'{{ device.displayName }} was {{ value }}°F'
|
||||||
if ( temperatureScale == 'C' )
|
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°C'
|
|
||||||
else
|
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°F'
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
name: name,
|
name: 'temperature',
|
||||||
value: value,
|
value: value,
|
||||||
descriptionText: descriptionText,
|
descriptionText: descriptionText,
|
||||||
translatable: true
|
translatable: true
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getContactResult(value) {
|
private Map getContactResult(value) {
|
||||||
log.debug "Contact: ${device.displayName} value = ${value}"
|
log.debug "Contact: ${device.displayName} value = ${value}"
|
||||||
def name = "contact"
|
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
|
||||||
|
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true)
|
||||||
def descriptionText
|
sendEvent(name: 'status', value: value, descriptionText: descriptionText, translatable: true)
|
||||||
if ( value == 'open' )
|
|
||||||
descriptionText = '{{ device.displayName }} was opened'
|
|
||||||
else
|
|
||||||
descriptionText = '{{ device.displayName }} was closed'
|
|
||||||
|
|
||||||
sendEvent(name: 'status', value: value, descriptionText: descriptionText, displayed: false)
|
|
||||||
def isStateChange = isStateChange(device, name, value)
|
|
||||||
return [
|
|
||||||
name: name,
|
|
||||||
value: value,
|
|
||||||
descriptionText: descriptionText,
|
|
||||||
isStateChange: isStateChange,
|
|
||||||
translatable: true
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAccelerationResult(numValue) {
|
private getAccelerationResult(numValue) {
|
||||||
log.debug "Acceleration is $value"
|
log.debug "Acceleration"
|
||||||
def name = "acceleration"
|
def name = "acceleration"
|
||||||
def value
|
def value
|
||||||
def descriptionText
|
def descriptionText
|
||||||
@@ -473,37 +451,38 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Device Reporting"
|
log.debug "Configuring Reporting"
|
||||||
|
|
||||||
def configCmds = [
|
def configCmds = [
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200",
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200",
|
||||||
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
|
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", "delay 200", //checkin time 6 hrs
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200",
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200",
|
||||||
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
|
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0xFC02 {${device.zigbeeId}} {}", "delay 200",
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0xFC02 {${device.zigbeeId}} {}", "delay 200",
|
||||||
"zcl mfg-code ${manufacturerCode}",
|
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||||
"zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}",
|
"zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
"zcl mfg-code ${manufacturerCode}",
|
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||||
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}",
|
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {0100}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
"zcl mfg-code ${manufacturerCode}",
|
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||||
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}",
|
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {0100}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
"zcl mfg-code ${manufacturerCode}",
|
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||||
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}",
|
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {0100}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
|
||||||
]
|
]
|
||||||
|
|
||||||
return configCmds + refresh()
|
return configCmds + refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,17 +498,12 @@ def enrollResponse() {
|
|||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
//Enroll Response
|
//Enroll Response
|
||||||
"raw 0x500 {01 23 00 00 00}",
|
"raw 0x500 {01 23 00 00 00}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
|
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseAxis(String description) {
|
private Map parseAxis(String description) {
|
||||||
def hexToSignedInt = { hexVal ->
|
|
||||||
def unsignedVal = hexToInt(hexVal)
|
|
||||||
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
|
||||||
}
|
|
||||||
|
|
||||||
def z = hexToSignedInt(description[0..3])
|
def z = hexToSignedInt(description[0..3])
|
||||||
def y = hexToSignedInt(description[10..13])
|
def y = hexToSignedInt(description[10..13])
|
||||||
def x = hexToSignedInt(description[20..23])
|
def x = hexToSignedInt(description[20..23])
|
||||||
@@ -556,6 +530,11 @@ private Map parseAxis(String description) {
|
|||||||
getXyzResult(xyzResults, description)
|
getXyzResult(xyzResults, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hexToSignedInt(hexVal) {
|
||||||
|
def unsignedVal = hexToInt(hexVal)
|
||||||
|
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
||||||
|
}
|
||||||
|
|
||||||
def garageEvent(zValue) {
|
def garageEvent(zValue) {
|
||||||
def absValue = zValue.abs()
|
def absValue = zValue.abs()
|
||||||
def contactValue = null
|
def contactValue = null
|
||||||
@@ -569,15 +548,9 @@ def garageEvent(zValue) {
|
|||||||
garageValue = 'garage-open'
|
garageValue = 'garage-open'
|
||||||
}
|
}
|
||||||
if (contactValue != null){
|
if (contactValue != null){
|
||||||
def linkText = getLinkText(device)
|
def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' :'{{ device.displayName }} was closed'
|
||||||
if ( contactValue == 'open' ) {
|
sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed:false, translatable: true)
|
||||||
descriptionText: '{{ device.displayName }} was opened'
|
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true)
|
||||||
sendEvent(name: 'contact', value: contactValue, descriptionText: '{{ device.displayName }} was opened', displayed:false, translatable: true)
|
|
||||||
sendEvent(name: 'status', value: garageValue, descriptionText: '{{ device.displayName }} status was opened', translatable: true)
|
|
||||||
} else {
|
|
||||||
sendEvent(name: 'contact', value: contactValue, descriptionText: '{{ device.displayName }} was closed', displayed:false, translatable: true)
|
|
||||||
sendEvent(name: 'status', value: garageValue, descriptionText: '{{ device.displayName }} status was closed', translatable: true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,11 +67,11 @@ mappings {
|
|||||||
def listAllDevices() {
|
def listAllDevices() {
|
||||||
def resp = []
|
def resp = []
|
||||||
switches.each {
|
switches.each {
|
||||||
resp << [name: it.name, label: it.label, value: it.currentValue("switch"), type: "switch", id: it.id, hub: it.hub.name]
|
resp << [name: it.name, label: it.label, value: it.currentValue("switch"), type: "switch", id: it.id, hub: it.hub?.name]
|
||||||
}
|
}
|
||||||
|
|
||||||
locks.each {
|
locks.each {
|
||||||
resp << [name: it.name, label: it.label, value: it.currentValue("lock"), type: "lock", id: it.id, hub: it.hub.name]
|
resp << [name: it.name, label: it.label, value: it.currentValue("lock"), type: "lock", id: it.id, hub: it.hub?.name]
|
||||||
}
|
}
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ def mainPage() {
|
|||||||
}
|
}
|
||||||
section{
|
section{
|
||||||
input "actionType", "enum", title: "Action?", required: true, defaultValue: "Bell 1", options: [
|
input "actionType", "enum", title: "Action?", required: true, defaultValue: "Bell 1", options: [
|
||||||
//"Custom Message",
|
"Custom Message",
|
||||||
"Bell 1",
|
"Bell 1",
|
||||||
"Bell 2",
|
"Bell 2",
|
||||||
"Dogs Barking",
|
"Dogs Barking",
|
||||||
@@ -89,7 +89,7 @@ def mainPage() {
|
|||||||
"Someone is arriving",
|
"Someone is arriving",
|
||||||
"Piano",
|
"Piano",
|
||||||
"Lightsaber"]
|
"Lightsaber"]
|
||||||
//input "message","text",title:"Play this message", required:false, multiple: false
|
input "message","text",title:"Play this message", required:false, multiple: false
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
input "sonos", "capability.musicPlayer", title: "On this Speaker player", required: true
|
input "sonos", "capability.musicPlayer", title: "On this Speaker player", required: true
|
||||||
@@ -408,13 +408,15 @@ private loadText() {
|
|||||||
case "Lightsaber":
|
case "Lightsaber":
|
||||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/lightsaber.mp3", duration: "10"]
|
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/lightsaber.mp3", duration: "10"]
|
||||||
break;
|
break;
|
||||||
default:
|
case "Custom Message":
|
||||||
/*if (message) {
|
if (message) {
|
||||||
state.sound = textToSpeech(message instanceof List ? message[0] : message) // not sure why this is (sometimes) needed)
|
state.sound = textToSpeech(message instanceof List ? message[0] : message) // not sure why this is (sometimes) needed)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
state.sound = textToSpeech("You selected the custom message option but did not enter a message in the $app.label Smart App")
|
state.sound = textToSpeech("You selected the custom message option but did not enter a message in the $app.label Smart App")
|
||||||
}*/
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell1.mp3", duration: "10"]
|
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell1.mp3", duration: "10"]
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user