mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-17 05:10:50 +00:00
Compare commits
1 Commits
PROD_2016.
...
MSA-1372-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef29820fa1 |
@@ -22,14 +22,12 @@ metadata {
|
|||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Health Check"
|
|
||||||
|
|
||||||
attribute "tamper", "enum", ["detected", "clear"]
|
attribute "tamper", "enum", ["detected", "clear"]
|
||||||
attribute "batteryStatus", "string"
|
attribute "batteryStatus", "string"
|
||||||
attribute "powerSupply", "enum", ["USB Cable", "Battery"]
|
attribute "powerSupply", "enum", ["USB Cable", "Battery"]
|
||||||
|
|
||||||
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A"
|
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A"
|
||||||
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A,0x5A"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
@@ -328,9 +326,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// allow device user configured or default 16 min to check in; double the periodic reporting interval
|
|
||||||
sendEvent(name: "checkInterval", value: 2* (timeOptionValueMap[reportInterval] ?: (2*8*60)), displayed: false)
|
|
||||||
|
|
||||||
// This sensor joins as a secure device if you double-click the button to include it
|
// This sensor joins as a secure device if you double-click the button to include it
|
||||||
log.debug "${device.displayName} is configuring its settings"
|
log.debug "${device.displayName} is configuring its settings"
|
||||||
def request = []
|
def request = []
|
||||||
@@ -357,7 +352,7 @@ def configure() {
|
|||||||
motionSensitivity == "minimum" ? 0 : 64)
|
motionSensitivity == "minimum" ? 0 : 64)
|
||||||
|
|
||||||
//5. report every x minutes (threshold reports don't work on battery power, default 8 mins)
|
//5. report every x minutes (threshold reports don't work on battery power, default 8 mins)
|
||||||
request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8*60)) //association group 1
|
request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: 8*60) //association group 1
|
||||||
|
|
||||||
request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6*60*60) //association group 2
|
request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6*60*60) //association group 2
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ metadata {
|
|||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Health Check"
|
|
||||||
|
|
||||||
command "configureAfterSecure"
|
|
||||||
|
|
||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x98,0x7A", outClusters:"0x5A"
|
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x98,0x7A", outClusters:"0x5A"
|
||||||
}
|
}
|
||||||
@@ -248,8 +245,6 @@ def configureAfterSecure() {
|
|||||||
def configure() {
|
def configure() {
|
||||||
// log.debug "configure()"
|
// log.debug "configure()"
|
||||||
//["delay 30000"] + secure(zwave.securityV1.securityCommandsSupportedGet())
|
//["delay 30000"] + secure(zwave.securityV1.securityCommandsSupportedGet())
|
||||||
// allow device 16 min to check in; double the periodic reporting interval
|
|
||||||
sendEvent(name: "checkInterval", value: 2*8*60, displayed: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setConfigured() {
|
private setConfigured() {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ metadata {
|
|||||||
capability "Illuminance Measurement"
|
capability "Illuminance Measurement"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Health Check"
|
|
||||||
|
|
||||||
fingerprint deviceId: "0x2001", inClusters: "0x30,0x31,0x80,0x84,0x70,0x85,0x72,0x86"
|
fingerprint deviceId: "0x2001", inClusters: "0x30,0x31,0x80,0x84,0x70,0x85,0x72,0x86"
|
||||||
}
|
}
|
||||||
@@ -181,9 +180,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// allow device 10 min to check in; double the periodic reporting interval
|
|
||||||
sendEvent(name: "checkInterval", value: 2*5*60, displayed: false)
|
|
||||||
|
|
||||||
delayBetween([
|
delayBetween([
|
||||||
// send binary sensor report instead of basic set for motion
|
// send binary sensor report instead of basic set for motion
|
||||||
zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: 2).format(),
|
zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: 2).format(),
|
||||||
|
|||||||
@@ -255,8 +255,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -271,8 +271,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ metadata {
|
|||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
|
|
||||||
command "enrollResponse"
|
command "enrollResponse"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ metadata {
|
|||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description: $description"
|
log.debug "description: $description"
|
||||||
|
|
||||||
Map map = [:]
|
Map map = [:]
|
||||||
if (description?.startsWith('catchall:')) {
|
if (description?.startsWith('catchall:')) {
|
||||||
map = parseCatchAllMessage(description)
|
map = parseCatchAllMessage(description)
|
||||||
@@ -87,10 +87,10 @@ def parse(String 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}"
|
||||||
@@ -128,7 +128,7 @@ 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)
|
||||||
@@ -141,7 +141,7 @@ private Map parseReportAttributeMessage(String description) {
|
|||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||||
}
|
}
|
||||||
log.debug "Desc Map: $descMap"
|
log.debug "Desc Map: $descMap"
|
||||||
|
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
||||||
def value = getTemperature(descMap.value)
|
def value = getTemperature(descMap.value)
|
||||||
@@ -153,11 +153,11 @@ private Map parseReportAttributeMessage(String description) {
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
private Map parseCustomMessage(String description) {
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
if (description?.startsWith('temperature: ')) {
|
if (description?.startsWith('temperature: ')) {
|
||||||
@@ -170,7 +170,7 @@ 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
|
||||||
@@ -240,8 +240,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,8 +338,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,8 +234,7 @@ def getTemperature(value) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,34 +13,32 @@
|
|||||||
* for the specific language governing permissions and limitations under the License.
|
* for the specific language governing permissions and limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
|
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Contact Sensor"
|
capability "Contact Sensor"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
capability "Health Check"
|
|
||||||
capability "Sensor"
|
|
||||||
|
|
||||||
command "enrollResponse"
|
command "enrollResponse"
|
||||||
|
|
||||||
|
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300-S"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300-S"
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300"
|
||||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3320-L", deviceJoinName: "Iris Contact Sensor"
|
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3320-L", deviceJoinName: "Iris Contact Sensor"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences {
|
preferences {
|
||||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){
|
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){
|
||||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||||
@@ -73,10 +71,10 @@ metadata {
|
|||||||
details(["contact","temperature","battery","refresh"])
|
details(["contact","temperature","battery","refresh"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description: $description"
|
log.debug "description: $description"
|
||||||
|
|
||||||
Map map = [:]
|
Map map = [:]
|
||||||
if (description?.startsWith('catchall:')) {
|
if (description?.startsWith('catchall:')) {
|
||||||
map = parseCatchAllMessage(description)
|
map = parseCatchAllMessage(description)
|
||||||
@@ -90,10 +88,10 @@ def parse(String 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}"
|
||||||
@@ -101,7 +99,7 @@ def parse(String description) {
|
|||||||
}
|
}
|
||||||
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)
|
||||||
@@ -127,7 +125,7 @@ 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)
|
||||||
@@ -137,14 +135,14 @@ private boolean shouldProcessMessage(cluster) {
|
|||||||
private int getHumidity(value) {
|
private int getHumidity(value) {
|
||||||
return Math.round(Double.parseDouble(value))
|
return Math.round(Double.parseDouble(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseReportAttributeMessage(String description) {
|
private Map parseReportAttributeMessage(String description) {
|
||||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||||
def nameAndValue = param.split(":")
|
def nameAndValue = param.split(":")
|
||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||||
}
|
}
|
||||||
log.debug "Desc Map: $descMap"
|
log.debug "Desc Map: $descMap"
|
||||||
|
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
||||||
def value = getTemperature(descMap.value)
|
def value = getTemperature(descMap.value)
|
||||||
@@ -153,10 +151,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
private Map parseCustomMessage(String description) {
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
if (description?.startsWith('temperature: ')) {
|
if (description?.startsWith('temperature: ')) {
|
||||||
@@ -169,7 +167,7 @@ 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
|
||||||
@@ -202,7 +200,7 @@ private Map parseIasMessage(String description) {
|
|||||||
}
|
}
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
def getTemperature(value) {
|
||||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
||||||
if(getTemperatureScale() == "C"){
|
if(getTemperatureScale() == "C"){
|
||||||
@@ -215,11 +213,11 @@ def getTemperature(value) {
|
|||||||
private Map getBatteryResult(rawValue) {
|
private Map getBatteryResult(rawValue) {
|
||||||
log.debug 'Battery'
|
log.debug 'Battery'
|
||||||
def linkText = getLinkText(device)
|
def linkText = getLinkText(device)
|
||||||
|
|
||||||
def result = [
|
def result = [
|
||||||
name: 'battery'
|
name: 'battery'
|
||||||
]
|
]
|
||||||
|
|
||||||
def volts = rawValue / 10
|
def volts = rawValue / 10
|
||||||
def descriptionText
|
def descriptionText
|
||||||
if (rawValue == 0 || rawValue == 255) {}
|
if (rawValue == 0 || rawValue == 255) {}
|
||||||
@@ -230,8 +228,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +273,6 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
sendEvent(name: "checkInterval", value: 7200, displayed: false)
|
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
|
|||||||
@@ -205,8 +205,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
* for the specific language governing permissions and limitations under the License.
|
* for the specific language governing permissions and limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
@@ -21,28 +21,28 @@ metadata {
|
|||||||
capability "Contact Sensor"
|
capability "Contact Sensor"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
|
|
||||||
command "enrollResponse"
|
command "enrollResponse"
|
||||||
|
|
||||||
|
|
||||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Visonic", model: "MCT-340 SMA"
|
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Visonic", model: "MCT-340 SMA"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
simulator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences {
|
preferences {
|
||||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles {
|
tiles {
|
||||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||||
}
|
}
|
||||||
|
|
||||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
||||||
state "temperature", label:'${currentValue}°',
|
state "temperature", label:'${currentValue}°',
|
||||||
backgroundColors:[
|
backgroundColors:[
|
||||||
@@ -58,23 +58,23 @@ metadata {
|
|||||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
|
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
|
||||||
state "battery", label:'${currentValue}% battery', unit:""
|
state "battery", label:'${currentValue}% battery', unit:""
|
||||||
}
|
}
|
||||||
|
|
||||||
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
|
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
}
|
}
|
||||||
|
|
||||||
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
|
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
|
||||||
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
||||||
}
|
}
|
||||||
|
|
||||||
main (["contact", "temperature"])
|
main (["contact", "temperature"])
|
||||||
details(["contact","temperature","battery","refresh","configure"])
|
details(["contact","temperature","battery","refresh","configure"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description: $description"
|
log.debug "description: $description"
|
||||||
|
|
||||||
Map map = [:]
|
Map map = [:]
|
||||||
if (description?.startsWith('catchall:')) {
|
if (description?.startsWith('catchall:')) {
|
||||||
map = parseCatchAllMessage(description)
|
map = parseCatchAllMessage(description)
|
||||||
@@ -88,10 +88,10 @@ def parse(String 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}"
|
||||||
@@ -99,7 +99,7 @@ def parse(String description) {
|
|||||||
}
|
}
|
||||||
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)
|
||||||
@@ -125,20 +125,20 @@ 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) {
|
||||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||||
def nameAndValue = param.split(":")
|
def nameAndValue = param.split(":")
|
||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||||
}
|
}
|
||||||
log.debug "Desc Map: $descMap"
|
log.debug "Desc Map: $descMap"
|
||||||
|
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
||||||
def value = getTemperature(descMap.value)
|
def value = getTemperature(descMap.value)
|
||||||
@@ -147,10 +147,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
private Map parseCustomMessage(String description) {
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
if (description?.startsWith('temperature: ')) {
|
if (description?.startsWith('temperature: ')) {
|
||||||
@@ -163,7 +163,7 @@ 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
|
||||||
@@ -196,7 +196,7 @@ private Map parseIasMessage(String description) {
|
|||||||
}
|
}
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
def getTemperature(value) {
|
||||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
||||||
if(getTemperatureScale() == "C"){
|
if(getTemperatureScale() == "C"){
|
||||||
@@ -209,11 +209,11 @@ def getTemperature(value) {
|
|||||||
private Map getBatteryResult(rawValue) {
|
private Map getBatteryResult(rawValue) {
|
||||||
log.debug 'Battery'
|
log.debug 'Battery'
|
||||||
def linkText = getLinkText(device)
|
def linkText = getLinkText(device)
|
||||||
|
|
||||||
def result = [
|
def result = [
|
||||||
name: 'battery'
|
name: 'battery'
|
||||||
]
|
]
|
||||||
|
|
||||||
def volts = rawValue / 10
|
def volts = rawValue / 10
|
||||||
def descriptionText
|
def descriptionText
|
||||||
if (volts > 3.5) {
|
if (volts > 3.5) {
|
||||||
@@ -223,8 +223,7 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +261,7 @@ def refresh()
|
|||||||
{
|
{
|
||||||
log.debug "Refreshing Temperature and Battery"
|
log.debug "Refreshing Temperature and Battery"
|
||||||
[
|
[
|
||||||
|
|
||||||
"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"
|
"st rattr 0x${device.deviceNetworkId} 1 1 0x20"
|
||||||
|
|
||||||
@@ -275,24 +274,24 @@ def configure() {
|
|||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
def configCmds = [
|
def configCmds = [
|
||||||
"delay 1000",
|
"delay 1000",
|
||||||
|
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
||||||
|
|
||||||
"zcl global send-me-a-report 1 0x20 0x20 600 3600 {01}", "delay 200",
|
"zcl global send-me-a-report 1 0x20 0x20 600 3600 {01}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
||||||
|
|
||||||
"zcl global send-me-a-report 0x402 0 0x29 300 3600 {6400}", "delay 200",
|
"zcl global send-me-a-report 0x402 0 0x29 300 3600 {6400}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
||||||
|
|
||||||
|
|
||||||
//"raw 0x500 {01 23 00 00 00}", "delay 200",
|
//"raw 0x500 {01 23 00 00 00}", "delay 200",
|
||||||
//"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
//"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
||||||
|
|
||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
"zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}",
|
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}",
|
||||||
|
|
||||||
"delay 500"
|
"delay 500"
|
||||||
]
|
]
|
||||||
return configCmds + enrollResponse() + refresh() // send refresh cmds as part of config
|
return configCmds + enrollResponse() + refresh() // send refresh cmds as part of config
|
||||||
@@ -300,11 +299,11 @@ def configure() {
|
|||||||
|
|
||||||
def enrollResponse() {
|
def enrollResponse() {
|
||||||
log.debug "Sending enroll response"
|
log.debug "Sending enroll response"
|
||||||
[
|
[
|
||||||
|
|
||||||
"raw 0x500 {01 23 00 00 00}", "delay 200",
|
"raw 0x500 {01 23 00 00 00}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1"
|
"send 0x${device.deviceNetworkId} 1 1"
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
private hex(value) {
|
private hex(value) {
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ metadata {
|
|||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x98"
|
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x98"
|
||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"
|
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"
|
||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x80,0x71,0x85,0x70,0x72,0x86,0x30,0x31,0x84,0x59,0x73,0x5A,0x8F,0x98,0x7A", outClusters:"0x20" // Philio multi+
|
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x80,0x71,0x85,0x70,0x72,0x86,0x30,0x31,0x84,0x59,0x73,0x5A,0x8F,0x98,0x7A", outClusters:"0x20" // Philio multi+
|
||||||
|
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x72,0x5A,0x80,0x73,0x86,0x84,0x85,0x59,0x71,0x70,0x7A,0x98" // Vision door/window
|
||||||
|
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x98,0x86,0x72,0x5A,0x85,0x59,0x73,0x80,0x71,0x31,0x70,0x84,0x7A" // Vision Motion
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulator metadata
|
// simulator metadata
|
||||||
@@ -35,7 +37,6 @@ metadata {
|
|||||||
// status messages
|
// status messages
|
||||||
status "open": "command: 2001, payload: FF"
|
status "open": "command: 2001, payload: FF"
|
||||||
status "closed": "command: 2001, payload: 00"
|
status "closed": "command: 2001, payload: 00"
|
||||||
status "wake up": "command: 8407, payload: "
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI tile definitions
|
// UI tile definitions
|
||||||
@@ -81,7 +82,7 @@ def updated() {
|
|||||||
def cmds = []
|
def cmds = []
|
||||||
if (!state.MSR) {
|
if (!state.MSR) {
|
||||||
cmds = [
|
cmds = [
|
||||||
command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()),
|
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
|
||||||
"delay 1200",
|
"delay 1200",
|
||||||
zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
||||||
]
|
]
|
||||||
@@ -94,9 +95,9 @@ def updated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
commands([
|
delayBetween([
|
||||||
zwave.manufacturerSpecificV2.manufacturerSpecificGet(),
|
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
|
||||||
zwave.batteryV1.batteryGet()
|
batteryGetCommand()
|
||||||
], 6000)
|
], 6000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,11 +148,12 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
|
|||||||
result << sensorValueEvent(1)
|
result << sensorValueEvent(1)
|
||||||
} else if (cmd.event == 0x03) {
|
} else if (cmd.event == 0x03) {
|
||||||
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
|
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
|
||||||
if(!state.MSR) result << response(command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
|
result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId))
|
||||||
|
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
||||||
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
|
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
|
||||||
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
|
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
|
||||||
} else if (cmd.event == 0x07) {
|
} else if (cmd.event == 0x07) {
|
||||||
if(!state.MSR) result << response(command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
|
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
||||||
result << createEvent(name: "motion", value: "active", descriptionText:"$device.displayName detected motion")
|
result << createEvent(name: "motion", value: "active", descriptionText:"$device.displayName detected motion")
|
||||||
}
|
}
|
||||||
} else if (cmd.notificationType) {
|
} else if (cmd.notificationType) {
|
||||||
@@ -169,11 +171,12 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd)
|
|||||||
def event = createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
|
def event = createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
|
||||||
def cmds = []
|
def cmds = []
|
||||||
if (!state.MSR) {
|
if (!state.MSR) {
|
||||||
cmds << command(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
cmds << zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId).format()
|
||||||
|
cmds << zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()
|
||||||
cmds << "delay 1200"
|
cmds << "delay 1200"
|
||||||
}
|
}
|
||||||
if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) {
|
if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) {
|
||||||
cmds << command(zwave.batteryV1.batteryGet())
|
cmds << batteryGetCommand()
|
||||||
} else {
|
} else {
|
||||||
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
||||||
}
|
}
|
||||||
@@ -210,7 +213,7 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
|
|||||||
if (msr == "0086-0102-0059") {
|
if (msr == "0086-0102-0059") {
|
||||||
result << response(zwave.securityV1.securityMessageEncapsulation().encapsulate(zwave.batteryV1.batteryGet()).format())
|
result << response(zwave.securityV1.securityMessageEncapsulation().encapsulate(zwave.batteryV1.batteryGet()).format())
|
||||||
} else {
|
} else {
|
||||||
result << response(command(zwave.batteryV1.batteryGet()))
|
result << response(batteryGetCommand())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +221,7 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
|
|||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 1, 0x30: 1, 0x31: 5, 0x80: 1, 0x84: 1, 0x71: 3, 0x9C: 1])
|
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])
|
||||||
// log.debug "encapsulated: $encapsulatedCommand"
|
// log.debug "encapsulated: $encapsulatedCommand"
|
||||||
if (encapsulatedCommand) {
|
if (encapsulatedCommand) {
|
||||||
state.sec = 1
|
state.sec = 1
|
||||||
@@ -230,16 +233,12 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
|||||||
createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
|
createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private command(physicalgraph.zwave.Command cmd) {
|
def batteryGetCommand() {
|
||||||
if (state.sec == 1) {
|
def cmd = zwave.batteryV1.batteryGet()
|
||||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
if (state.sec) {
|
||||||
} else {
|
cmd = zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd)
|
||||||
cmd.format()
|
|
||||||
}
|
}
|
||||||
}
|
cmd.format()
|
||||||
|
|
||||||
private commands(commands, delay=200) {
|
|
||||||
delayBetween(commands.collect{ command(it) }, delay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def retypeBasedOnMSR() {
|
def retypeBasedOnMSR() {
|
||||||
@@ -262,12 +261,12 @@ def retypeBasedOnMSR() {
|
|||||||
setDeviceType("3-in-1 Multisensor Plus (SG)")
|
setDeviceType("3-in-1 Multisensor Plus (SG)")
|
||||||
break
|
break
|
||||||
case "0109-2001-0106": // Vision door/window
|
case "0109-2001-0106": // Vision door/window
|
||||||
log.debug "Changing device type to Z-Wave Plus Door/Window Sensor"
|
log.debug "Changing device type to Door / Window Sensor Plus (SG)"
|
||||||
setDeviceType("Z-Wave Plus Door/Window Sensor")
|
setDeviceType("Door / Window Sensor Plus (SG)")
|
||||||
break
|
break
|
||||||
case "0109-2002-0205": // Vision Motion
|
case "0109-2002-0205": // Vision Motion
|
||||||
log.debug "Changing device type to Z-Wave Plus Motion/Temp Sensor"
|
log.debug "Changing device type to Vision Motion Sensor Plus (SG)"
|
||||||
setDeviceType("Z-Wave Plus Motion/Temp Sensor")
|
setDeviceType("Vision Motion Sensor Plus (SG)")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,270 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2016 SmartThings
|
|
||||||
* Copyright 2015 AstraLink
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Z-Wave Plus Door/Window Sensor, ZD2102*-5
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
metadata {
|
|
||||||
definition (name: "Z-Wave Plus Door/Window Sensor", namespace: "smartthings", author: "SmartThings") {
|
|
||||||
capability "Contact Sensor"
|
|
||||||
capability "Configuration"
|
|
||||||
capability "Battery"
|
|
||||||
capability "Sensor"
|
|
||||||
|
|
||||||
// for Astralink
|
|
||||||
attribute "ManufacturerCode", "string"
|
|
||||||
attribute "ProduceTypeCode", "string"
|
|
||||||
attribute "ProductCode", "string"
|
|
||||||
attribute "WakeUp", "string"
|
|
||||||
attribute "WirelessConfig", "string"
|
|
||||||
|
|
||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x98, 0x86, 0x72, 0x5A, 0x85, 0x59, 0x73, 0x80, 0x71, 0x70, 0x84, 0x7A"
|
|
||||||
fingerprint type:"8C07", inClusters: "5E,98,86,72,5A,71"
|
|
||||||
fingerprint mfr:"0109", prod:"2001", model:"0106" // not using deviceJoinName because it's sold under different brand names
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles(scale: 2) {
|
|
||||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){
|
|
||||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
|
||||||
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
|
|
||||||
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
|
||||||
state "battery", label:'${currentValue}% battery', unit:""
|
|
||||||
}
|
|
||||||
|
|
||||||
main (["contact"])
|
|
||||||
details(["contact","battery"])
|
|
||||||
}
|
|
||||||
|
|
||||||
simulator {
|
|
||||||
// messages the device returns in response to commands it receives
|
|
||||||
status "open (basic)" : "command: 9881, payload: 00 20 01 FF"
|
|
||||||
status "closed (basic)" : "command: 9881 payload: 00 20 01 00"
|
|
||||||
status "open (notification)" : "command: 9881, payload: 00 71 05 06 FF 00 FF 06 16 00 00"
|
|
||||||
status "closed (notification)" : "command: 9881, payload: 00 71 05 06 00 00 FF 06 17 00 00"
|
|
||||||
status "tamper: enclosure opened" : "command: 9881, payload: 00 71 05 07 FF 00 FF 07 03 00 00"
|
|
||||||
status "tamper: enclosure replaced" : "command: 9881, payload: 00 71 05 07 00 00 FF 07 00 00 00"
|
|
||||||
status "wake up" : "command: 9881, payload: 00 84 07"
|
|
||||||
status "battery (100%)" : "command: 9881, payload: 00 80 03 64"
|
|
||||||
status "battery low" : "command: 9881, payload: 00 80 03 FF"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def configure() {
|
|
||||||
log.debug "configure()"
|
|
||||||
def cmds = []
|
|
||||||
|
|
||||||
if (state.sec != 1) {
|
|
||||||
// secure inclusion may not be complete yet
|
|
||||||
cmds << "delay 1000"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmds += secureSequence([
|
|
||||||
zwave.manufacturerSpecificV2.manufacturerSpecificGet(),
|
|
||||||
zwave.batteryV1.batteryGet(),
|
|
||||||
], 500)
|
|
||||||
|
|
||||||
cmds << "delay 8000"
|
|
||||||
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCommandClassVersions() {
|
|
||||||
[
|
|
||||||
0x71: 3, // Notification
|
|
||||||
0x5E: 2, // ZwaveplusInfo
|
|
||||||
0x59: 1, // AssociationGrpInfo
|
|
||||||
0x85: 2, // Association
|
|
||||||
0x20: 1, // Basic
|
|
||||||
0x80: 1, // Battery
|
|
||||||
0x70: 1, // Configuration
|
|
||||||
0x5A: 1, // DeviceResetLocally
|
|
||||||
0x7A: 2, // FirmwareUpdateMd
|
|
||||||
0x72: 2, // ManufacturerSpecific
|
|
||||||
0x73: 1, // Powerlevel
|
|
||||||
0x98: 1, // Security
|
|
||||||
0x84: 2, // WakeUp
|
|
||||||
0x86: 1, // Version
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse incoming device messages to generate events
|
|
||||||
def parse(String description) {
|
|
||||||
def result = []
|
|
||||||
def cmd
|
|
||||||
if (description.startsWith("Err 106")) {
|
|
||||||
state.sec = 0
|
|
||||||
result = createEvent( name: "secureInclusion", value: "failed", eventType: "ALERT",
|
|
||||||
descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.")
|
|
||||||
} else if (description.startsWith("Err")) {
|
|
||||||
result = createEvent(descriptionText: "$device.displayName $description", isStateChange: true)
|
|
||||||
} else {
|
|
||||||
cmd = zwave.parse(description, commandClassVersions)
|
|
||||||
if (cmd) {
|
|
||||||
result = zwaveEvent(cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result instanceof List) {
|
|
||||||
result = result.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug "Parsed '$description' to $result"
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
|
||||||
def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions)
|
|
||||||
log.debug "encapsulated: $encapsulatedCommand"
|
|
||||||
if (encapsulatedCommand) {
|
|
||||||
state.sec = 1
|
|
||||||
return zwaveEvent(encapsulatedCommand)
|
|
||||||
} else {
|
|
||||||
log.warn "Unable to extract encapsulated cmd from $cmd"
|
|
||||||
return [createEvent(descriptionText: cmd.toString())]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def sensorValueEvent(value) {
|
|
||||||
if (value) {
|
|
||||||
createEvent(name: "contact", value: "open", descriptionText: "$device.displayName is open")
|
|
||||||
} else {
|
|
||||||
createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
|
||||||
return sensorValueEvent(cmd.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
|
|
||||||
return sensorValueEvent(cmd.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) {
|
|
||||||
return sensorValueEvent(cmd.sensorValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) {
|
|
||||||
return sensorValueEvent(cmd.sensorState)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
|
||||||
def result = []
|
|
||||||
if (cmd.notificationType == 0x06 && cmd.event == 0x16) {
|
|
||||||
result << sensorValueEvent(1)
|
|
||||||
} else if (cmd.notificationType == 0x06 && cmd.event == 0x17) {
|
|
||||||
result << sensorValueEvent(0)
|
|
||||||
} else if (cmd.notificationType == 0x07) {
|
|
||||||
if (cmd.event == 0x00) {
|
|
||||||
if (cmd.eventParametersLength == 0 || cmd.eventParameter[0] != 3) {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName covering replaced", isStateChange: true, displayed: false)
|
|
||||||
} else {
|
|
||||||
result << sensorValueEvent(0)
|
|
||||||
}
|
|
||||||
} else if (cmd.event == 0x01 || cmd.event == 0x02) {
|
|
||||||
result << sensorValueEvent(1)
|
|
||||||
} else if (cmd.event == 0x03) {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
|
|
||||||
if (!device.currentState("ManufacturerCode")) {
|
|
||||||
result << response(secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
|
|
||||||
}
|
|
||||||
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
|
|
||||||
} else {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName event $cmd.event ${cmd.eventParameter.inspect()}", isStateChange: true, displayed: false)
|
|
||||||
}
|
|
||||||
} else if (cmd.notificationType) {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName notification $cmd.notificationType event $cmd.event ${cmd.eventParameter.inspect()}", isStateChange: true, displayed: false)
|
|
||||||
} else {
|
|
||||||
def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"
|
|
||||||
result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, isStateChange: true, displayed: false)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
|
|
||||||
def event = createEvent(name: "WakeUp", value: "wakeup", descriptionText: "${device.displayName} woke up", isStateChange: true, displayed: false) // for Astralink
|
|
||||||
def cmds = []
|
|
||||||
|
|
||||||
if (!device.currentState("ManufacturerCode")) {
|
|
||||||
cmds << secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
|
||||||
cmds << "delay 2000"
|
|
||||||
}
|
|
||||||
if (!state.lastbat || now() - state.lastbat > 10*60*60*1000) {
|
|
||||||
event.descriptionText += ", requesting battery"
|
|
||||||
cmds << secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1))
|
|
||||||
cmds << "delay 800"
|
|
||||||
cmds << secure(zwave.batteryV1.batteryGet())
|
|
||||||
cmds << "delay 2000"
|
|
||||||
} else {
|
|
||||||
log.debug "not checking battery, was updated ${(now() - state.lastbat)/60000 as int} min ago"
|
|
||||||
}
|
|
||||||
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
|
||||||
|
|
||||||
return [event, response(cmds)]
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
|
||||||
def map = [ name: "battery", unit: "%" ]
|
|
||||||
if (cmd.batteryLevel == 0xFF) {
|
|
||||||
map.value = 1
|
|
||||||
map.descriptionText = "${device.displayName} has a low battery"
|
|
||||||
map.isStateChange = true
|
|
||||||
} else {
|
|
||||||
map.value = cmd.batteryLevel
|
|
||||||
}
|
|
||||||
def event = createEvent(map)
|
|
||||||
|
|
||||||
// Save at least one battery report in events list every few days
|
|
||||||
if (!event.isStateChange && (now() - 3*24*60*60*1000) > device.latestState("battery")?.date?.time) {
|
|
||||||
map.isStateChange = true
|
|
||||||
}
|
|
||||||
state.lastbat = now()
|
|
||||||
return [event]
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
|
||||||
def result = []
|
|
||||||
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
|
|
||||||
def productTypeCode = String.format("%04X", cmd.productTypeId)
|
|
||||||
def productCode = String.format("%04X", cmd.productId)
|
|
||||||
def wirelessConfig = "ZWP"
|
|
||||||
log.debug "MSR ${manufacturerCode} ${productTypeCode} ${productCode}"
|
|
||||||
|
|
||||||
result << createEvent(name: "ManufacturerCode", value: manufacturerCode)
|
|
||||||
result << createEvent(name: "ProduceTypeCode", value: productTypeCode)
|
|
||||||
result << createEvent(name: "ProductCode", value: productCode)
|
|
||||||
result << createEvent(name: "WirelessConfig", value: wirelessConfig)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
|
||||||
return [createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)]
|
|
||||||
}
|
|
||||||
|
|
||||||
private secure(physicalgraph.zwave.Command cmd) {
|
|
||||||
if (state.sec == 0) { // default to secure
|
|
||||||
cmd.format()
|
|
||||||
} else {
|
|
||||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private secureSequence(commands, delay=200) {
|
|
||||||
delayBetween(commands.collect{ secure(it) }, delay)
|
|
||||||
}
|
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2016 SmartThings
|
|
||||||
* Copyright 2015 AstraLink
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Z-Wave Plus Motion Sensor with Temperature Measurement, ZP3102*-5
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
metadata {
|
|
||||||
definition (name: "Z-Wave Plus Motion/Temp Sensor", namespace: "smartthings", author: "SmartThings") {
|
|
||||||
capability "Motion Sensor"
|
|
||||||
capability "Temperature Measurement"
|
|
||||||
capability "Configuration"
|
|
||||||
capability "Battery"
|
|
||||||
capability "Sensor"
|
|
||||||
|
|
||||||
// for Astralink
|
|
||||||
attribute "ManufacturerCode", "string"
|
|
||||||
attribute "ProduceTypeCode", "string"
|
|
||||||
attribute "ProductCode", "string"
|
|
||||||
attribute "WakeUp", "string"
|
|
||||||
attribute "WirelessConfig", "string"
|
|
||||||
|
|
||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x98, 0x86, 0x72, 0x5A, 0x85, 0x59, 0x73, 0x80, 0x71, 0x31, 0x70, 0x84, 0x7A"
|
|
||||||
fingerprint type:"8C07", inClusters: "5E,98,86,72,5A,31,71"
|
|
||||||
fingerprint mfr:"0109", prod:"2002", model:"0205" // not using deviceJoinName because it's sold under different brand names
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles {
|
|
||||||
standardTile("motion", "device.motion", width: 3, height: 2) {
|
|
||||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
|
||||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
|
||||||
}
|
|
||||||
|
|
||||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
|
||||||
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") {
|
|
||||||
state "battery", label:'${currentValue}% battery', unit:"%"
|
|
||||||
}
|
|
||||||
|
|
||||||
main(["motion", "temperature"])
|
|
||||||
details(["motion", "temperature", "battery"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated() {
|
|
||||||
if (!device.currentState("ManufacturerCode")) {
|
|
||||||
response(secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def configure() {
|
|
||||||
log.debug "configure()"
|
|
||||||
def cmds = []
|
|
||||||
|
|
||||||
if (state.sec != 1) {
|
|
||||||
// secure inclusion may not be complete yet
|
|
||||||
cmds << "delay 1000"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmds += secureSequence([
|
|
||||||
zwave.manufacturerSpecificV2.manufacturerSpecificGet(),
|
|
||||||
zwave.batteryV1.batteryGet(),
|
|
||||||
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1)
|
|
||||||
], 500)
|
|
||||||
|
|
||||||
cmds << "delay 8000"
|
|
||||||
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCommandClassVersions() {
|
|
||||||
[
|
|
||||||
0x71: 3, // Notification
|
|
||||||
0x5E: 2, // ZwaveplusInfo
|
|
||||||
0x59: 1, // AssociationGrpInfo
|
|
||||||
0x85: 2, // Association
|
|
||||||
0x20: 1, // Basic
|
|
||||||
0x80: 1, // Battery
|
|
||||||
0x70: 1, // Configuration
|
|
||||||
0x5A: 1, // DeviceResetLocally
|
|
||||||
0x7A: 2, // FirmwareUpdateMd
|
|
||||||
0x72: 2, // ManufacturerSpecific
|
|
||||||
0x73: 1, // Powerlevel
|
|
||||||
0x98: 1, // Security
|
|
||||||
0x31: 5, // SensorMultilevel
|
|
||||||
0x84: 2 // WakeUp
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse incoming device messages to generate events
|
|
||||||
def parse(String description) {
|
|
||||||
def result = []
|
|
||||||
def cmd
|
|
||||||
if (description.startsWith("Err 106")) {
|
|
||||||
state.sec = 0
|
|
||||||
result = createEvent( name: "secureInclusion", value: "failed", eventType: "ALERT",
|
|
||||||
descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.")
|
|
||||||
} else if (description.startsWith("Err")) {
|
|
||||||
result = createEvent(descriptionText: "$device.displayName $description", isStateChange: true)
|
|
||||||
} else {
|
|
||||||
cmd = zwave.parse(description, commandClassVersions)
|
|
||||||
if (cmd) {
|
|
||||||
result = zwaveEvent(cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result instanceof List) {
|
|
||||||
result = result.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug "Parsed '$description' to $result"
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
|
||||||
def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions)
|
|
||||||
log.debug "encapsulated: $encapsulatedCommand"
|
|
||||||
if (encapsulatedCommand) {
|
|
||||||
state.sec = 1
|
|
||||||
return zwaveEvent(encapsulatedCommand)
|
|
||||||
} else {
|
|
||||||
log.warn "Unable to extract encapsulated cmd from $cmd"
|
|
||||||
return [createEvent(descriptionText: cmd.toString())]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def sensorValueEvent(value) {
|
|
||||||
def result = []
|
|
||||||
if (value) {
|
|
||||||
log.debug "sensorValueEvent($value) : active"
|
|
||||||
result << createEvent(name: "motion", value: "active", descriptionText: "$device.displayName detected motion")
|
|
||||||
} else {
|
|
||||||
log.debug "sensorValueEvent($value) : inactive"
|
|
||||||
result << createEvent(name: "motion", value: "inactive", descriptionText: "$device.displayName motion has stopped")
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
|
||||||
return sensorValueEvent(cmd.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
|
|
||||||
return sensorValueEvent(cmd.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
|
|
||||||
return sensorValueEvent(cmd.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) {
|
|
||||||
return sensorValueEvent(cmd.sensorValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd) {
|
|
||||||
return sensorValueEvent(cmd.sensorState)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
|
||||||
def result = []
|
|
||||||
if (cmd.notificationType == 0x07) {
|
|
||||||
if (cmd.event == 0x01 || cmd.event == 0x02) {
|
|
||||||
result << sensorValueEvent(1)
|
|
||||||
} else if (cmd.event == 0x03) {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
|
|
||||||
result << response(secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet()))
|
|
||||||
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
|
|
||||||
} else if (cmd.event == 0x07) {
|
|
||||||
result << sensorValueEvent(1)
|
|
||||||
} else if (cmd.event == 0x08) {
|
|
||||||
result << sensorValueEvent(1)
|
|
||||||
} else if (cmd.event == 0x00) {
|
|
||||||
if (cmd.eventParametersLength && cmd.eventParameter[0] == 3) {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName covering replaced", isStateChange: true, displayed: false)
|
|
||||||
} else {
|
|
||||||
result << sensorValueEvent(0)
|
|
||||||
}
|
|
||||||
} else if (cmd.event == 0xFF) {
|
|
||||||
result << sensorValueEvent(1)
|
|
||||||
} else {
|
|
||||||
result << createEvent(descriptionText: "$device.displayName sent event $cmd.event")
|
|
||||||
}
|
|
||||||
} else if (cmd.notificationType) {
|
|
||||||
def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}"
|
|
||||||
result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false)
|
|
||||||
} else {
|
|
||||||
def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"
|
|
||||||
result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
|
|
||||||
def event = createEvent(name: "WakeUp", value: "wakeup", descriptionText: "${device.displayName} woke up", isStateChange: true, displayed: false) // for Astralink
|
|
||||||
def cmds = []
|
|
||||||
|
|
||||||
if (!device.currentState("ManufacturerCode")) {
|
|
||||||
cmds << secure(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
|
||||||
cmds << "delay 2000"
|
|
||||||
}
|
|
||||||
if (!state.lastbat || now() - state.lastbat > 10*60*60*1000) {
|
|
||||||
event.descriptionText += ", requesting battery"
|
|
||||||
cmds << secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1))
|
|
||||||
cmds << "delay 800"
|
|
||||||
cmds << secure(zwave.batteryV1.batteryGet())
|
|
||||||
cmds << "delay 2000"
|
|
||||||
} else {
|
|
||||||
log.debug "not checking battery, was updated ${(now() - state.lastbat)/60000 as int} min ago"
|
|
||||||
}
|
|
||||||
cmds << secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
|
||||||
|
|
||||||
return [event, response(cmds)]
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
|
||||||
def result = []
|
|
||||||
def map = [ name: "battery", unit: "%" ]
|
|
||||||
if (cmd.batteryLevel == 0xFF) {
|
|
||||||
map.value = 1
|
|
||||||
map.descriptionText = "${device.displayName} has a low battery"
|
|
||||||
map.isStateChange = true
|
|
||||||
} else {
|
|
||||||
map.value = cmd.batteryLevel
|
|
||||||
}
|
|
||||||
def event = createEvent(map)
|
|
||||||
|
|
||||||
// Save at least one battery report in events list every few days
|
|
||||||
if (!event.isStateChange && (now() - 3*24*60*60*1000) > device.latestState("battery")?.date?.time) {
|
|
||||||
map.isStateChange = true
|
|
||||||
}
|
|
||||||
state.lastbat = now()
|
|
||||||
return [event]
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
|
|
||||||
def result = []
|
|
||||||
def map = [:]
|
|
||||||
switch (cmd.sensorType) {
|
|
||||||
case 1:
|
|
||||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
|
||||||
map.name = "temperature"
|
|
||||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
|
|
||||||
map.unit = getTemperatureScale()
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
map.name = "illuminance"
|
|
||||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
|
||||||
map.unit = "lux"
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
map.name = "humidity"
|
|
||||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
|
||||||
map.unit = cmd.scale == 0 ? "%" : ""
|
|
||||||
break;
|
|
||||||
case 0x1E:
|
|
||||||
map.name = "loudness"
|
|
||||||
map.unit = cmd.scale == 1 ? "dBA" : "dB"
|
|
||||||
map.value = cmd.scaledSensorValue.toString()
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
map.descriptionText = cmd.toString()
|
|
||||||
}
|
|
||||||
result << createEvent(map)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
|
||||||
def result = []
|
|
||||||
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
|
|
||||||
def productTypeCode = String.format("%04X", cmd.productTypeId)
|
|
||||||
def productCode = String.format("%04X", cmd.productId)
|
|
||||||
def wirelessConfig = "ZWP"
|
|
||||||
log.debug "MSR ${manufacturerCode} ${productTypeCode} ${productCode}"
|
|
||||||
|
|
||||||
result << createEvent(name: "ManufacturerCode", value: manufacturerCode)
|
|
||||||
result << createEvent(name: "ProduceTypeCode", value: productTypeCode)
|
|
||||||
result << createEvent(name: "ProductCode", value: productCode)
|
|
||||||
result << createEvent(name: "WirelessConfig", value: wirelessConfig)
|
|
||||||
|
|
||||||
if (manufacturerCode == "0109" && productTypeCode == "2002") {
|
|
||||||
result << response(secureSequence([
|
|
||||||
// Change re-trigger duration to 1 minute
|
|
||||||
zwave.configurationV1.configurationSet(parameterNumber: 1, configurationValue: [1], size: 1),
|
|
||||||
zwave.batteryV1.batteryGet(),
|
|
||||||
zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1)
|
|
||||||
], 400))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
|
||||||
return [createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)]
|
|
||||||
}
|
|
||||||
|
|
||||||
private secure(physicalgraph.zwave.Command cmd) {
|
|
||||||
if (state.sec == 0) { // default to secure
|
|
||||||
cmd.format()
|
|
||||||
} else {
|
|
||||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private secureSequence(commands, delay=200) {
|
|
||||||
delayBetween(commands.collect{ secure(it) }, delay)
|
|
||||||
}
|
|
||||||
@@ -24,17 +24,24 @@ definition(
|
|||||||
iconX3Url: "http://www.gidjit.com/appicon@3x.png",
|
iconX3Url: "http://www.gidjit.com/appicon@3x.png",
|
||||||
oauth: [displayName: "Gidjit", displayLink: "www.gidjit.com"])
|
oauth: [displayName: "Gidjit", displayLink: "www.gidjit.com"])
|
||||||
|
|
||||||
|
preferences(oauthPage: "deviceAuthorization") {
|
||||||
|
// deviceAuthorization page is simply the devices to authorize
|
||||||
|
page(name: "deviceAuthorization", title: "Device Authorization", nextPage: "instructionPage",
|
||||||
|
install: false, uninstall: true) {
|
||||||
|
section ("Allow Gidjit to have access, thereby allowing you to quickly control and monitor your following devices. Privacy Policy can be found at http://priv.gidjit.com/privacy.html") {
|
||||||
|
input "switches", "capability.switch", title: "Control/Monitor your switches", multiple: true, required: false
|
||||||
|
input "thermostats", "capability.thermostat", title: "Control/Monitor your thermostats", multiple: true, required: false
|
||||||
|
input "windowShades", "capability.windowShade", title: "Control/Monitor your window shades", multiple: true, required: false //windowShade
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
preferences {
|
page(name: "instructionPage", title: "Device Discovery", install: true) {
|
||||||
section ("Allow Gidjit to have access, there by allowing you to quickly control and monitor the following devices") {
|
section() {
|
||||||
input "switches", "capability.switch", title: "Control/Monitor your switches", multiple: true, required: false
|
paragraph "Now the process is complete return to the Devices section of the Detected Screen. From there and you can add actions to each of your device panels, including launching SmartThings routines."
|
||||||
input "thermostats", "capability.thermostat", title: "Control/Monitor your thermostats", multiple: true, required: false
|
}
|
||||||
input "windowShades", "capability.windowShade", title: "Control/Monitor your window shades", multiple: true, required: false //windowShade
|
}
|
||||||
//input "bulbs", "capability.colorControl", title: "Control your lights", multiple: true, required: false //windowShade
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mappings {
|
mappings {
|
||||||
path("/structureinfo") {
|
path("/structureinfo") {
|
||||||
action: [
|
action: [
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ def completionPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
section("Notifications") {
|
section("Notifications") {
|
||||||
input("recipients", "contact", title: "Send notifications to", required: false) {
|
input("recipients", "contact", title: "Send notifications to") {
|
||||||
input(name: "completionPhoneNumber", type: "phone", title: "Text This Number", description: "Phone number", required: false)
|
input(name: "completionPhoneNumber", type: "phone", title: "Text This Number", description: "Phone number", required: false)
|
||||||
input(name: "completionPush", type: "bool", title: "Send A Push Notification", description: "Phone number", required: false)
|
input(name: "completionPush", type: "bool", title: "Send A Push Notification", description: "Phone number", required: false)
|
||||||
}
|
}
|
||||||
@@ -720,7 +720,7 @@ def completionPercentage() {
|
|||||||
|
|
||||||
def now = new Date().getTime()
|
def now = new Date().getTime()
|
||||||
def timeElapsed = now - atomicState.start
|
def timeElapsed = now - atomicState.start
|
||||||
def totalRunTime = totalRunTimeMillis() ?: 1
|
def totalRunTime = totalRunTimeMillis()
|
||||||
def percentComplete = timeElapsed / totalRunTime * 100
|
def percentComplete = timeElapsed / totalRunTime * 100
|
||||||
log.debug "percentComplete: ${percentComplete}"
|
log.debug "percentComplete: ${percentComplete}"
|
||||||
|
|
||||||
|
|||||||
@@ -689,7 +689,7 @@ def parse(childDevice, description) {
|
|||||||
log.warn "Parsing Body failed - trying again..."
|
log.warn "Parsing Body failed - trying again..."
|
||||||
poll()
|
poll()
|
||||||
}
|
}
|
||||||
if (body instanceof java.util.Map) {
|
if (body instanceof java.util.HashMap) {
|
||||||
//poll response
|
//poll response
|
||||||
def bulbs = getChildDevices()
|
def bulbs = getChildDevices()
|
||||||
for (bulb in body) {
|
for (bulb in body) {
|
||||||
@@ -830,22 +830,22 @@ def setColorTemperature(childDevice, huesettings) {
|
|||||||
|
|
||||||
def setColor(childDevice, huesettings) {
|
def setColor(childDevice, huesettings) {
|
||||||
log.debug "Executing 'setColor($huesettings)'"
|
log.debug "Executing 'setColor($huesettings)'"
|
||||||
|
|
||||||
def value = [:]
|
def value = [:]
|
||||||
def hue = null
|
def hue = null
|
||||||
def sat = null
|
def sat = null
|
||||||
def xy = null
|
def xy = null
|
||||||
|
|
||||||
if (huesettings.hex != null) {
|
if (huesettings.hex != null) {
|
||||||
value.xy = getHextoXY(huesettings.hex)
|
value.xy = getHextoXY(huesettings.hex)
|
||||||
} else {
|
} else {
|
||||||
if (huesettings.hue != null)
|
if (huesettings.hue != null)
|
||||||
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
|
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
|
||||||
if (huesettings.saturation != null)
|
if (huesettings.saturation != null)
|
||||||
value.sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
|
value.sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default behavior is to turn light on
|
// Default behavior is to turn light on
|
||||||
value.on = true
|
value.on = true
|
||||||
|
|
||||||
if (huesettings.level != null) {
|
if (huesettings.level != null) {
|
||||||
@@ -853,7 +853,7 @@ def setColor(childDevice, huesettings) {
|
|||||||
value.on = false
|
value.on = false
|
||||||
else if (huesettings.level == 1)
|
else if (huesettings.level == 1)
|
||||||
value.bri = 1
|
value.bri = 1
|
||||||
else
|
else
|
||||||
value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
|
value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
|
||||||
}
|
}
|
||||||
value.alert = huesettings.alert ? huesettings.alert : "none"
|
value.alert = huesettings.alert ? huesettings.alert : "none"
|
||||||
|
|||||||
@@ -688,7 +688,7 @@ def validateCommand(device, command) {
|
|||||||
def capabilityCommands = getDeviceCapabilityCommands(device.capabilities)
|
def capabilityCommands = getDeviceCapabilityCommands(device.capabilities)
|
||||||
def currentDeviceCapability = getCapabilityName(device)
|
def currentDeviceCapability = getCapabilityName(device)
|
||||||
if (currentDeviceCapability != "" && capabilityCommands[currentDeviceCapability]) {
|
if (currentDeviceCapability != "" && capabilityCommands[currentDeviceCapability]) {
|
||||||
return (command in capabilityCommands[currentDeviceCapability] || (currentDeviceCapability == "Switch" && command == "setLevel" && device.hasCommand("setLevel"))) ? true : false
|
return command in capabilityCommands[currentDeviceCapability] ? true : false
|
||||||
} else {
|
} else {
|
||||||
// Handling other device types here, which don't accept commands
|
// Handling other device types here, which don't accept commands
|
||||||
httpError(400, "Bad request.")
|
httpError(400, "Bad request.")
|
||||||
@@ -823,8 +823,8 @@ def deviceHandler(evt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def sendToHarmony(evt, String callbackUrl) {
|
def sendToHarmony(evt, String callbackUrl) {
|
||||||
def callback = new URI(callbackUrl)
|
def callback = new URI(callbackUrl)
|
||||||
if (callback.port != -1) {
|
if(isIP(callback.host)){
|
||||||
def host = callback.port != -1 ? "${callback.host}:${callback.port}" : callback.host
|
def host = callback.port != -1 ? "${callback.host}:${callback.port}" : callback.host
|
||||||
def path = callback.query ? "${callback.path}?${callback.query}".toString() : callback.path
|
def path = callback.query ? "${callback.path}?${callback.query}".toString() : callback.path
|
||||||
sendHubCommand(new physicalgraph.device.HubAction(
|
sendHubCommand(new physicalgraph.device.HubAction(
|
||||||
@@ -852,6 +852,25 @@ def sendToHarmony(evt, String callbackUrl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isIP(String str) {
|
||||||
|
try {
|
||||||
|
String[] parts = str.split("\\.");
|
||||||
|
if (parts.length != 4) return false;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
int p
|
||||||
|
try {
|
||||||
|
p = Integer.parseInt(parts[i]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (p > 255 || p < 0) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def listHubs() {
|
def listHubs() {
|
||||||
location.hubs?.findAll { it.type.toString() == "PHYSICAL" }?.collect { hubItem(it) }
|
location.hubs?.findAll { it.type.toString() == "PHYSICAL" }?.collect { hubItem(it) }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user