mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-04-04 14:23:08 +01:00
Clean up smartsense DTHs
This continues an effort to clean up the SmartSense DTHs and move as much of the logic as possible into the library. This simplifies the DTH and has the advantage of having only a single location where issues need to be fixed.
This commit is contained in:
@@ -69,292 +69,35 @@ metadata {
|
|||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description is $description"
|
log.debug "description is $description"
|
||||||
|
|
||||||
def event = [:]
|
def event = zigbee.getEvent(description)
|
||||||
def finalResult = isKnownDescription(description)
|
if (!event) {
|
||||||
if (finalResult) {
|
|
||||||
log.info finalResult
|
|
||||||
if (finalResult.type == "update") {
|
|
||||||
log.info "$device updates: ${finalResult.value}"
|
|
||||||
event = null
|
|
||||||
}
|
|
||||||
else if (finalResult.type == "power") {
|
|
||||||
def powerValue = (finalResult.value as Integer)/10
|
|
||||||
event = createEvent(name: "power", value: powerValue)
|
|
||||||
|
|
||||||
/*
|
|
||||||
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
|
|
||||||
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event = createEvent(name: finalResult.type, value: finalResult.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||||
log.debug parseDescriptionAsMap(description)
|
log.debug zigbee.parseDescriptionAsMap(description)
|
||||||
|
} else if (event.name == "power") {
|
||||||
|
/*
|
||||||
|
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. Simplifying to 10 power level is an integer.
|
||||||
|
*/
|
||||||
|
event.value = event.value / 10
|
||||||
}
|
}
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commands to device
|
def setLevel(value) {
|
||||||
def zigbeeCommand(cluster, attribute){
|
zigbee.setLevel(value)
|
||||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} ${cluster} ${attribute} {}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def off() {
|
def off() {
|
||||||
zigbeeCommand("6", "0")
|
zigbee.off()
|
||||||
}
|
}
|
||||||
|
|
||||||
def on() {
|
def on() {
|
||||||
zigbeeCommand("6", "1")
|
zigbee.on()
|
||||||
}
|
|
||||||
|
|
||||||
def setLevel(value) {
|
|
||||||
value = value as Integer
|
|
||||||
if (value == 0) {
|
|
||||||
off()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (device.latestValue("switch") == "off") {
|
|
||||||
sendEvent(name: "switch", value: "on")
|
|
||||||
}
|
|
||||||
sendEvent(name: "level", value: value)
|
|
||||||
setLevelWithRate(value, "0000") //value is between 0 to 100
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
[
|
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.electricMeasurementPowerRefresh()
|
||||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 2000",
|
|
||||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 2000",
|
|
||||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0B04 0x050B", "delay 2000"
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
refresh() + onOffConfig() + levelConfig() + powerConfig()
|
refresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.electricMeasurementPowerConfig()
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private getEndpointId() {
|
|
||||||
new BigInteger(device.endpointId, 16).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private hex(value, width=2) {
|
|
||||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
|
||||||
while (s.size() < width) {
|
|
||||||
s = "0" + s
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
private String swapEndianHex(String hex) {
|
|
||||||
reverseArray(hex.decodeHex()).encodeHex()
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer convertHexToInt(hex) {
|
|
||||||
Integer.parseInt(hex,16)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Need to reverse array of size 2
|
|
||||||
private byte[] reverseArray(byte[] array) {
|
|
||||||
byte tmp;
|
|
||||||
tmp = array[1];
|
|
||||||
array[1] = array[0];
|
|
||||||
array[0] = tmp;
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
|
|
||||||
def parseDescriptionAsMap(description) {
|
|
||||||
if (description?.startsWith("read attr -")) {
|
|
||||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
|
||||||
def nameAndValue = param.split(":")
|
|
||||||
map += [(nameAndValue[0].trim()): nameAndValue[1].trim()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (description?.startsWith("catchall: ")) {
|
|
||||||
def seg = (description - "catchall: ").split(" ")
|
|
||||||
def zigbeeMap = [:]
|
|
||||||
zigbeeMap += [raw: (description - "catchall: ")]
|
|
||||||
zigbeeMap += [profileId: seg[0]]
|
|
||||||
zigbeeMap += [clusterId: seg[1]]
|
|
||||||
zigbeeMap += [sourceEndpoint: seg[2]]
|
|
||||||
zigbeeMap += [destinationEndpoint: seg[3]]
|
|
||||||
zigbeeMap += [options: seg[4]]
|
|
||||||
zigbeeMap += [messageType: seg[5]]
|
|
||||||
zigbeeMap += [dni: seg[6]]
|
|
||||||
zigbeeMap += [isClusterSpecific: Short.valueOf(seg[7], 16) != 0]
|
|
||||||
zigbeeMap += [isManufacturerSpecific: Short.valueOf(seg[8], 16) != 0]
|
|
||||||
zigbeeMap += [manufacturerId: seg[9]]
|
|
||||||
zigbeeMap += [command: seg[10]]
|
|
||||||
zigbeeMap += [direction: seg[11]]
|
|
||||||
zigbeeMap += [data: seg.size() > 12 ? seg[12].split("").findAll { it }.collate(2).collect {
|
|
||||||
it.join('')
|
|
||||||
} : []]
|
|
||||||
|
|
||||||
zigbeeMap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def isKnownDescription(description) {
|
|
||||||
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
|
|
||||||
def descMap = parseDescriptionAsMap(description)
|
|
||||||
if (descMap.cluster == "0006" || descMap.clusterId == "0006") {
|
|
||||||
isDescriptionOnOff(descMap)
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "0008" || descMap.clusterId == "0008"){
|
|
||||||
isDescriptionLevel(descMap)
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "0B04" || descMap.clusterId == "0B04"){
|
|
||||||
isDescriptionPower(descMap)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(description?.startsWith("on/off:")) {
|
|
||||||
def switchValue = description?.endsWith("1") ? "on" : "off"
|
|
||||||
return [type: "switch", value : switchValue]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def isDescriptionOnOff(descMap) {
|
|
||||||
def switchValue = "undefined"
|
|
||||||
if (descMap.cluster == "0006") { //cluster info from read attr
|
|
||||||
value = descMap.value
|
|
||||||
if (value == "01"){
|
|
||||||
switchValue = "on"
|
|
||||||
}
|
|
||||||
else if (value == "00"){
|
|
||||||
switchValue = "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (descMap.clusterId == "0006") {
|
|
||||||
//cluster info from catch all
|
|
||||||
//command 0B is Default response and the last two bytes are [on/off][success]. on/off=00, success=00
|
|
||||||
//command 01 is Read attr response. the last two bytes are [datatype][value]. boolean datatype=10; on/off value = 01/00
|
|
||||||
if ((descMap.command=="0B" && descMap.raw.endsWith("0100")) || (descMap.command=="01" && descMap.raw.endsWith("1001"))){
|
|
||||||
switchValue = "on"
|
|
||||||
}
|
|
||||||
else if ((descMap.command=="0B" && descMap.raw.endsWith("0000")) || (descMap.command=="01" && descMap.raw.endsWith("1000"))){
|
|
||||||
switchValue = "off"
|
|
||||||
}
|
|
||||||
else if(descMap.command=="07"){
|
|
||||||
return [type: "update", value : "switch (0006) capability configured successfully"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (switchValue != "undefined"){
|
|
||||||
return [type: "switch", value : switchValue]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//@return - false or "success" or level [0-100]
|
|
||||||
def isDescriptionLevel(descMap) {
|
|
||||||
def dimmerValue = -1
|
|
||||||
if (descMap.cluster == "0008"){
|
|
||||||
//TODO: the message returned with catchall is command 0B with clusterId 0008. That is just a confirmation message
|
|
||||||
def value = convertHexToInt(descMap.value)
|
|
||||||
dimmerValue = Math.round(value * 100 / 255)
|
|
||||||
if(dimmerValue==0 && value > 0) {
|
|
||||||
dimmerValue = 1 //handling for non-zero hex value less than 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(descMap.clusterId == "0008") {
|
|
||||||
if(descMap.command=="0B"){
|
|
||||||
return [type: "update", value : "level updated successfully"] //device updating the level change was successful. no value sent.
|
|
||||||
}
|
|
||||||
else if(descMap.command=="07"){
|
|
||||||
return [type: "update", value : "level (0008) capability configured successfully"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dimmerValue != -1){
|
|
||||||
return [type: "level", value : dimmerValue]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def isDescriptionPower(descMap) {
|
|
||||||
def powerValue = "undefined"
|
|
||||||
if (descMap.cluster == "0B04") {
|
|
||||||
if (descMap.attrId == "050b") {
|
|
||||||
if(descMap.value!="ffff")
|
|
||||||
powerValue = convertHexToInt(descMap.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (descMap.clusterId == "0B04") {
|
|
||||||
if(descMap.command=="07"){
|
|
||||||
return [type: "update", value : "power (0B04) capability configured successfully"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (powerValue != "undefined"){
|
|
||||||
return [type: "power", value : powerValue]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def onOffConfig() {
|
|
||||||
[
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 2000",
|
|
||||||
"zcl global send-me-a-report 6 0 0x10 0 600 {01}", "delay 200",
|
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
|
|
||||||
//min level change is 01
|
|
||||||
def levelConfig() {
|
|
||||||
[
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 8 {${device.zigbeeId}} {}", "delay 2000",
|
|
||||||
"zcl global send-me-a-report 8 0 0x20 5 3600 {01}", "delay 200",
|
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
|
||||||
//min change in value is 05
|
|
||||||
def powerConfig() {
|
|
||||||
[
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 2000",
|
|
||||||
"zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}", //The send-me-a-report is custom to the attribute type for CentraLite
|
|
||||||
"delay 200",
|
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def setLevelWithRate(level, rate) {
|
|
||||||
if(rate == null){
|
|
||||||
rate = "0000"
|
|
||||||
}
|
|
||||||
level = convertToHexString(level * 255 / 100) //Converting the 0-100 range to 0-FF range in hex
|
|
||||||
[
|
|
||||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {$level $rate}",
|
|
||||||
"delay 2000"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
String convertToHexString(value, width=2) {
|
|
||||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
|
||||||
while (s.size() < width) {
|
|
||||||
s = "0" + s
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
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") {
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Switch"
|
capability "Switch"
|
||||||
capability "Power Meter"
|
capability "Power Meter"
|
||||||
@@ -24,9 +24,9 @@ metadata {
|
|||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Health Check"
|
capability "Health Check"
|
||||||
|
|
||||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"
|
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"
|
||||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet"
|
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet"
|
||||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet"
|
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet"
|
||||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019"
|
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,32 +44,32 @@ metadata {
|
|||||||
preferences {
|
preferences {
|
||||||
section {
|
section {
|
||||||
image(name: 'educationalcontent', multiple: true, images: [
|
image(name: 'educationalcontent', multiple: true, images: [
|
||||||
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS1.jpg",
|
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS1.jpg",
|
||||||
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS2.jpg"
|
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS2.jpg"
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI tile definitions
|
// UI tile definitions
|
||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
|
||||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
|
||||||
attributeState "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
attributeState "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||||
attributeState "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
attributeState "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||||
attributeState "turningOn", label: 'Turning On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
attributeState "turningOn", label: 'Turning On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||||
attributeState "turningOff", label: 'Turning Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
attributeState "turningOff", label: 'Turning Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||||
}
|
}
|
||||||
tileAttribute ("power", key: "SECONDARY_CONTROL") {
|
tileAttribute("power", key: "SECONDARY_CONTROL") {
|
||||||
attributeState "power", label:'${currentValue} W'
|
attributeState "power", label: '${currentValue} W'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "switch"
|
main "switch"
|
||||||
details(["switch","refresh"])
|
details(["switch", "refresh"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,47 +77,29 @@ metadata {
|
|||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description is $description"
|
log.debug "description is $description"
|
||||||
|
|
||||||
def finalResult = zigbee.getKnownDescription(description)
|
def event = zigbee.getEvent(description)
|
||||||
def event = [:]
|
|
||||||
|
|
||||||
//TODO: Remove this after getKnownDescription can parse it automatically
|
if (event) {
|
||||||
if (!finalResult && description!="updated")
|
if (event.name == "power") {
|
||||||
finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description))
|
event.value = event.value / 10
|
||||||
|
event.descriptionText = '{{ device.displayName }} power is {{ value }} Watts'
|
||||||
if (finalResult) {
|
event.translatable = true
|
||||||
log.info "final result = $finalResult"
|
} else if (event.name == "switch") {
|
||||||
if (finalResult.type == "update") {
|
def descriptionText = event.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
||||||
log.info "$device updates: ${finalResult.value}"
|
event = createEvent(name: event.name, value: event.value, descriptionText: descriptionText, translatable: true)
|
||||||
event = null
|
|
||||||
}
|
}
|
||||||
else if (finalResult.type == "power") {
|
} else {
|
||||||
def powerValue = (finalResult.value as Integer)/10
|
|
||||||
event = createEvent(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
|
|
||||||
power level is an integer. The exact power level with correct units needs to be handled in the device type
|
|
||||||
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
|
||||||
event = createEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
def cluster = zigbee.parse(description)
|
def cluster = zigbee.parse(description)
|
||||||
|
|
||||||
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07){
|
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
|
||||||
if (cluster.data[0] == 0x00) {
|
if (cluster.data[0] == 0x00) {
|
||||||
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
|
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
|
||||||
event = createEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
event = createEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
||||||
event = null
|
event = null
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||||
log.debug "${cluster}"
|
log.debug "${cluster}"
|
||||||
}
|
}
|
||||||
@@ -152,29 +134,3 @@ def configure() {
|
|||||||
refresh() + zigbee.onOffConfig(0, 300) + zigbee.electricMeasurementPowerConfig()
|
refresh() + zigbee.onOffConfig(0, 300) + zigbee.electricMeasurementPowerConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
private getEndpointId() {
|
|
||||||
new BigInteger(device.endpointId, 16).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Remove this after getKnownDescription can parse it automatically
|
|
||||||
def getPowerDescription(descMap) {
|
|
||||||
def powerValue = "undefined"
|
|
||||||
if (descMap.cluster == "0B04") {
|
|
||||||
if (descMap.attrId == "050b") {
|
|
||||||
if(descMap.value!="ffff")
|
|
||||||
powerValue = zigbee.convertHexToInt(descMap.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (descMap.clusterId == "0B04") {
|
|
||||||
if(descMap.command=="07"){
|
|
||||||
return [type: "update", value : "power (0B04) capability configured successfully"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (powerValue != "undefined"){
|
|
||||||
return [type: "power", value : powerValue]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
|||||||
|
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings") {
|
definition(name: "SmartSense Moisture Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
@@ -43,10 +43,10 @@ metadata {
|
|||||||
preferences {
|
preferences {
|
||||||
section {
|
section {
|
||||||
image(name: 'educationalcontent', multiple: true, images: [
|
image(name: 'educationalcontent', multiple: true, images: [
|
||||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture1.png",
|
"http://cdn.device-gse.smartthings.com/Moisture/Moisture1.png",
|
||||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture2.png",
|
"http://cdn.device-gse.smartthings.com/Moisture/Moisture2.png",
|
||||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture3.png"
|
"http://cdn.device-gse.smartthings.com/Moisture/Moisture3.png"
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
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"
|
||||||
@@ -55,32 +55,32 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){
|
multiAttributeTile(name: "water", type: "generic", width: 6, height: 4) {
|
||||||
tileAttribute ("device.water", key: "PRIMARY_CONTROL") {
|
tileAttribute("device.water", key: "PRIMARY_CONTROL") {
|
||||||
attributeState "dry", label: "Dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
|
attributeState "dry", label: "Dry", icon: "st.alarm.water.dry", backgroundColor: "#ffffff"
|
||||||
attributeState "wet", label: "Wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
|
attributeState "wet", label: "Wet", icon: "st.alarm.water.wet", backgroundColor: "#53a7c0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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"],
|
||||||
[value: 59, color: "#90d2a7"],
|
[value: 59, color: "#90d2a7"],
|
||||||
[value: 74, color: "#44b621"],
|
[value: 74, color: "#44b621"],
|
||||||
[value: 84, color: "#f1d801"],
|
[value: 84, color: "#f1d801"],
|
||||||
[value: 95, color: "#d04e00"],
|
[value: 95, color: "#d04e00"],
|
||||||
[value: 96, color: "#bc2323"]
|
[value: 96, color: "#bc2323"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
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 (["water", "temperature"])
|
main(["water", "temperature"])
|
||||||
details(["water", "temperature", "battery", "refresh"])
|
details(["water", "temperature", "battery", "refresh"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,18 +88,24 @@ metadata {
|
|||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description: $description"
|
log.debug "description: $description"
|
||||||
|
|
||||||
Map map = [:]
|
// getEvent will handle temperature and humidity
|
||||||
if (description?.startsWith('catchall:')) {
|
Map map = zigbee.getEvent(description)
|
||||||
map = parseCatchAllMessage(description)
|
if (!map) {
|
||||||
}
|
if (description?.startsWith('zone status')) {
|
||||||
else if (description?.startsWith('read attr -')) {
|
map = parseIasMessage(description)
|
||||||
map = parseReportAttributeMessage(description)
|
} else {
|
||||||
}
|
Map descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
else if (description?.startsWith('temperature: ')) {
|
if (descMap.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) {
|
||||||
map = parseCustomMessage(description)
|
map = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
}
|
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
|
||||||
else if (description?.startsWith('zone status')) {
|
if (descMap.data[0] == "00") {
|
||||||
map = parseIasMessage(description)
|
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
} else {
|
||||||
|
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
@@ -113,92 +119,12 @@ def parse(String description) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
def cluster = zigbee.parse(description)
|
|
||||||
if (shouldProcessMessage(cluster)) {
|
|
||||||
switch(cluster.clusterId) {
|
|
||||||
case 0x0001:
|
|
||||||
// 0x07 - configure reporting
|
|
||||||
if (cluster.command != 0x07) {
|
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0x0402:
|
|
||||||
if (cluster.command == 0x07) {
|
|
||||||
if (cluster.data[0] == 0x00){
|
|
||||||
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
|
|
||||||
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// temp is last 2 data values. reverse to swap endian
|
|
||||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
|
||||||
def value = getTemperature(temp)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
|
||||||
// 0x0B is default response indicating message got through
|
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
|
||||||
cluster.command == 0x0B ||
|
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
|
||||||
return !ignoredMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseReportAttributeMessage(String description) {
|
|
||||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
|
||||||
def nameAndValue = param.split(":")
|
|
||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
|
||||||
}
|
|
||||||
log.debug "Desc Map: $descMap"
|
|
||||||
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
|
||||||
def value = getTemperature(descMap.value)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
|
||||||
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (description?.startsWith('temperature: ')) {
|
|
||||||
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseIasMessage(String description) {
|
private Map parseIasMessage(String description) {
|
||||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||||
|
|
||||||
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry')
|
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry')
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
|
||||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
|
||||||
if(getTemperatureScale() == "C"){
|
|
||||||
return Math.round(celsius)
|
|
||||||
} else {
|
|
||||||
return Math.round(celsiusToFahrenheit(celsius))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getBatteryResult(rawValue) {
|
private Map getBatteryResult(rawValue) {
|
||||||
log.debug "Battery rawValue = ${rawValue}"
|
log.debug "Battery rawValue = ${rawValue}"
|
||||||
def linkText = getLinkText(device)
|
def linkText = getLinkText(device)
|
||||||
@@ -239,40 +165,18 @@ private Map getBatteryResult(rawValue) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getTemperatureResult(value) {
|
|
||||||
log.debug 'TEMP'
|
|
||||||
if (tempOffset) {
|
|
||||||
def offset = tempOffset as int
|
|
||||||
def v = value as int
|
|
||||||
value = v + offset
|
|
||||||
}
|
|
||||||
def descriptionText
|
|
||||||
if ( temperatureScale == 'C' )
|
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°C'
|
|
||||||
else
|
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°F'
|
|
||||||
|
|
||||||
return [
|
|
||||||
name: 'temperature',
|
|
||||||
value: value,
|
|
||||||
descriptionText: descriptionText,
|
|
||||||
translatable: true,
|
|
||||||
unit: temperatureScale
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getMoistureResult(value) {
|
private Map getMoistureResult(value) {
|
||||||
log.debug "water"
|
log.debug "water"
|
||||||
def descriptionText
|
def descriptionText
|
||||||
if ( value == "wet" )
|
if (value == "wet")
|
||||||
descriptionText = '{{ device.displayName }} is wet'
|
descriptionText = '{{ device.displayName }} is wet'
|
||||||
else
|
else
|
||||||
descriptionText = '{{ device.displayName }} is dry'
|
descriptionText = '{{ device.displayName }} is dry'
|
||||||
return [
|
return [
|
||||||
name: 'water',
|
name : 'water',
|
||||||
value: value,
|
value : value,
|
||||||
descriptionText: descriptionText,
|
descriptionText: descriptionText,
|
||||||
translatable: true
|
translatable : true
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +190,7 @@ def ping() {
|
|||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "Refreshing Temperature and Battery"
|
log.debug "Refreshing Temperature and Battery"
|
||||||
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
||||||
|
|
||||||
return refreshCmds + zigbee.enrollResponse()
|
return refreshCmds + zigbee.enrollResponse()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
|||||||
|
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition(name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Motion Sensor"
|
capability "Motion Sensor"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
@@ -45,10 +45,10 @@ metadata {
|
|||||||
preferences {
|
preferences {
|
||||||
section {
|
section {
|
||||||
image(name: 'educationalcontent', multiple: true, images: [
|
image(name: 'educationalcontent', multiple: true, images: [
|
||||||
"http://cdn.device-gse.smartthings.com/Motion/Motion1.jpg",
|
"http://cdn.device-gse.smartthings.com/Motion/Motion1.jpg",
|
||||||
"http://cdn.device-gse.smartthings.com/Motion/Motion2.jpg",
|
"http://cdn.device-gse.smartthings.com/Motion/Motion2.jpg",
|
||||||
"http://cdn.device-gse.smartthings.com/Motion/Motion3.jpg"
|
"http://cdn.device-gse.smartthings.com/Motion/Motion3.jpg"
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
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"
|
||||||
@@ -57,30 +57,30 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){
|
multiAttributeTile(name: "motion", type: "generic", width: 6, height: 4) {
|
||||||
tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
|
tileAttribute("device.motion", key: "PRIMARY_CONTROL") {
|
||||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
attributeState "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#53a7c0"
|
||||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
attributeState "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#ffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||||
state("temperature", label:'${currentValue}°', unit:"F",
|
state("temperature", label: '${currentValue}°', unit: "F",
|
||||||
backgroundColors:[
|
backgroundColors: [
|
||||||
[value: 31, color: "#153591"],
|
[value: 31, color: "#153591"],
|
||||||
[value: 44, color: "#1e9cbb"],
|
[value: 44, color: "#1e9cbb"],
|
||||||
[value: 59, color: "#90d2a7"],
|
[value: 59, color: "#90d2a7"],
|
||||||
[value: 74, color: "#44b621"],
|
[value: 74, color: "#44b621"],
|
||||||
[value: 84, color: "#f1d801"],
|
[value: 84, color: "#f1d801"],
|
||||||
[value: 95, color: "#d04e00"],
|
[value: 95, color: "#d04e00"],
|
||||||
[value: 96, color: "#bc2323"]
|
[value: 96, color: "#bc2323"]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
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(["motion", "temperature"])
|
main(["motion", "temperature"])
|
||||||
@@ -90,19 +90,27 @@ metadata {
|
|||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description: $description"
|
log.debug "description: $description"
|
||||||
|
Map map = zigbee.getEvent(description)
|
||||||
Map map = [:]
|
if (!map) {
|
||||||
if (description?.startsWith('catchall:')) {
|
if (description?.startsWith('zone status')) {
|
||||||
map = parseCatchAllMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
} else {
|
||||||
else if (description?.startsWith('read attr -')) {
|
Map descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
map = parseReportAttributeMessage(description)
|
if (descMap?.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) {
|
||||||
}
|
map = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
else if (description?.startsWith('temperature: ')) {
|
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
|
||||||
map = parseCustomMessage(description)
|
if (descMap.data[0] == "00") {
|
||||||
}
|
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
|
||||||
else if (description?.startsWith('zone status')) {
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
map = parseIasMessage(description)
|
} else {
|
||||||
|
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||||
|
}
|
||||||
|
} else if (descMap.clusterInt == 0x0406 && descMap.attrInt == 0x0000) {
|
||||||
|
def value = descMap.value.endsWith("01") ? "active" : "inactive"
|
||||||
|
log.warn "Doing a read attr motion event"
|
||||||
|
resultMap = getMotionResult(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
@@ -116,90 +124,6 @@ def parse(String description) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
def cluster = zigbee.parse(description)
|
|
||||||
if (shouldProcessMessage(cluster)) {
|
|
||||||
switch(cluster.clusterId) {
|
|
||||||
case 0x0001:
|
|
||||||
// 0x07 - configure reporting
|
|
||||||
if (cluster.command != 0x07) {
|
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0x0402:
|
|
||||||
if (cluster.command == 0x07) {
|
|
||||||
if (cluster.data[0] == 0x00) {
|
|
||||||
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
|
|
||||||
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// temp is last 2 data values. reverse to swap endian
|
|
||||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
|
||||||
def value = getTemperature(temp)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0x0406:
|
|
||||||
// 0x07 - configure reporting
|
|
||||||
if (cluster.command != 0x07) {
|
|
||||||
log.debug 'motion'
|
|
||||||
resultMap.name = 'motion'
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
|
||||||
// 0x0B is default response indicating message got through
|
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
|
||||||
cluster.command == 0x0B ||
|
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
|
||||||
return !ignoredMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseReportAttributeMessage(String description) {
|
|
||||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
|
||||||
def nameAndValue = param.split(":")
|
|
||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
|
||||||
}
|
|
||||||
log.debug "Desc Map: $descMap"
|
|
||||||
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
|
||||||
def value = getTemperature(descMap.value)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
|
||||||
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "0406" && descMap.attrId == "0000") {
|
|
||||||
def value = descMap.value.endsWith("01") ? "active" : "inactive"
|
|
||||||
resultMap = getMotionResult(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (description?.startsWith('temperature: ')) {
|
|
||||||
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseIasMessage(String description) {
|
private Map parseIasMessage(String description) {
|
||||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||||
|
|
||||||
@@ -207,15 +131,6 @@ private Map parseIasMessage(String description) {
|
|||||||
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive')
|
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive')
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
|
||||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
|
||||||
if(getTemperatureScale() == "C"){
|
|
||||||
return Math.round(celsius)
|
|
||||||
} else {
|
|
||||||
return Math.round(celsiusToFahrenheit(celsius))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getBatteryResult(rawValue) {
|
private Map getBatteryResult(rawValue) {
|
||||||
log.debug "Battery rawValue = ${rawValue}"
|
log.debug "Battery rawValue = ${rawValue}"
|
||||||
def linkText = getLinkText(device)
|
def linkText = getLinkText(device)
|
||||||
@@ -255,36 +170,14 @@ private Map getBatteryResult(rawValue) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getTemperatureResult(value) {
|
|
||||||
log.debug 'TEMP'
|
|
||||||
if (tempOffset) {
|
|
||||||
def offset = tempOffset as int
|
|
||||||
def v = value as int
|
|
||||||
value = v + offset
|
|
||||||
}
|
|
||||||
def descriptionText
|
|
||||||
if ( temperatureScale == 'C' )
|
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°C'
|
|
||||||
else
|
|
||||||
descriptionText = '{{ device.displayName }} was {{ value }}°F'
|
|
||||||
|
|
||||||
return [
|
|
||||||
name: 'temperature',
|
|
||||||
value: value,
|
|
||||||
descriptionText: descriptionText,
|
|
||||||
translatable: true,
|
|
||||||
unit: temperatureScale
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
||||||
return [
|
return [
|
||||||
name: 'motion',
|
name : 'motion',
|
||||||
value: value,
|
value : value,
|
||||||
descriptionText: descriptionText,
|
descriptionText: descriptionText,
|
||||||
translatable: true
|
translatable : true
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +192,7 @@ def refresh() {
|
|||||||
log.debug "refresh called"
|
log.debug "refresh called"
|
||||||
|
|
||||||
def refreshCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
def refreshCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
||||||
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)
|
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)
|
||||||
|
|
||||||
return refreshCmds + zigbee.enrollResponse()
|
return refreshCmds + zigbee.enrollResponse()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,20 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
|||||||
import physicalgraph.zigbee.zcl.DataType
|
import physicalgraph.zigbee.zcl.DataType
|
||||||
|
|
||||||
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"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
capability "Health Check"
|
capability "Health Check"
|
||||||
|
|
||||||
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"
|
||||||
@@ -58,11 +58,11 @@ metadata {
|
|||||||
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",
|
||||||
"http://cdn.device-gse.smartthings.com/Multi/Multi2.jpg",
|
"http://cdn.device-gse.smartthings.com/Multi/Multi2.jpg",
|
||||||
"http://cdn.device-gse.smartthings.com/Multi/Multi3.jpg",
|
"http://cdn.device-gse.smartthings.com/Multi/Multi3.jpg",
|
||||||
"http://cdn.device-gse.smartthings.com/Multi/Multi4.jpg"
|
"http://cdn.device-gse.smartthings.com/Multi/Multi4.jpg"
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
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"
|
||||||
@@ -74,58 +74,74 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
|
multiAttributeTile(name: "status", type: "generic", width: 6, height: 4) {
|
||||||
tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
|
tileAttribute("device.status", key: "PRIMARY_CONTROL") {
|
||||||
attributeState "open", label:'Open', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
|
attributeState "open", label: 'Open', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
|
||||||
attributeState "closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
|
attributeState "closed", label: 'Closed', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
|
||||||
attributeState "garage-open", label:'Open', icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e"
|
attributeState "garage-open", label: 'Open', icon: "st.doors.garage.garage-open", backgroundColor: "#ffa81e"
|
||||||
attributeState "garage-closed", label:'Closed', icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821"
|
attributeState "garage-closed", label: 'Closed', icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||||
state("open", label:'Open', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
state("open", label: 'Open', icon: "st.contact.contact.open", backgroundColor: "#ffa81e")
|
||||||
state("closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
state("closed", label: 'Closed', icon: "st.contact.contact.closed", backgroundColor: "#79b821")
|
||||||
}
|
}
|
||||||
standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
|
standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
|
||||||
state("active", label:'Active', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
|
state("active", label: 'Active', icon: "st.motion.acceleration.active", backgroundColor: "#53a7c0")
|
||||||
state("inactive", label:'Inactive', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
|
state("inactive", label: 'Inactive', icon: "st.motion.acceleration.inactive", backgroundColor: "#ffffff")
|
||||||
}
|
}
|
||||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
valueTile("temperature", "device.temperature", 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"],
|
||||||
[value: 59, color: "#90d2a7"],
|
[value: 59, color: "#90d2a7"],
|
||||||
[value: 74, color: "#44b621"],
|
[value: 74, color: "#44b621"],
|
||||||
[value: 84, color: "#f1d801"],
|
[value: 84, color: "#f1d801"],
|
||||||
[value: 95, color: "#d04e00"],
|
[value: 95, color: "#d04e00"],
|
||||||
[value: 96, color: "#bc2323"]
|
[value: 96, color: "#bc2323"]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
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", "battery", "refresh"])
|
details(["status", "acceleration", "temperature", "battery", "refresh"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
Map map = [:]
|
Map map = zigbee.getEvent(description)
|
||||||
if (description?.startsWith('catchall:')) {
|
if (!map) {
|
||||||
map = parseCatchAllMessage(description)
|
if (description?.startsWith('zone status')) {
|
||||||
}
|
map = parseIasMessage(description)
|
||||||
else if (description?.startsWith('temperature: ')) {
|
} else {
|
||||||
map = parseCustomMessage(description)
|
Map descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
}
|
if (descMap?.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) {
|
||||||
else if (description?.startsWith('zone status')) {
|
map = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
map = parseIasMessage(description)
|
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
|
||||||
|
if (descMap.data[0] == "00") {
|
||||||
|
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
} else {
|
||||||
|
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
map = handleAcceleration(descMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (map.name == "temperature") {
|
||||||
|
if (tempOffset) {
|
||||||
|
map.value = (int) map.value + (int) tempOffset
|
||||||
|
}
|
||||||
|
map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F'
|
||||||
|
map.translatable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
def result = map ? createEvent(map) : [:]
|
def result = map ? createEvent(map) : [:]
|
||||||
@@ -135,72 +151,12 @@ def parse(String description) {
|
|||||||
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 -')) {
|
|
||||||
result = parseReportAttributeMessage(description).each { createEvent(it) }
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
private Map handleAcceleration(descMap) {
|
||||||
Map resultMap = [:]
|
Map result = [:]
|
||||||
def cluster = zigbee.parse(description)
|
if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0010) {
|
||||||
log.debug cluster
|
|
||||||
if (shouldProcessMessage(cluster)) {
|
|
||||||
switch(cluster.clusterId) {
|
|
||||||
case 0x0001:
|
|
||||||
// 0x07 - configure reporting
|
|
||||||
if (cluster.command != 0x07) {
|
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0xFC02:
|
|
||||||
log.debug 'ACCELERATION'
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0x0402:
|
|
||||||
if (cluster.command == 0x07) {
|
|
||||||
if(cluster.data[0] == 0x00) {
|
|
||||||
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
|
|
||||||
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// temp is last 2 data values. reverse to swap endian
|
|
||||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
|
||||||
def value = getTemperature(temp)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
|
||||||
// 0x0B is default response indicating message got through
|
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
|
||||||
cluster.command == 0x0B ||
|
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
|
||||||
return !ignoredMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
private List parseReportAttributeMessage(String description) {
|
|
||||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
|
||||||
def nameAndValue = param.split(":")
|
|
||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
|
||||||
}
|
|
||||||
|
|
||||||
List result = []
|
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
|
||||||
def value = getTemperature(descMap.value)
|
|
||||||
result << getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "FC02" && descMap.attrId == "0010") {
|
|
||||||
if (descMap.value.size() == 32) {
|
if (descMap.value.size() == 32) {
|
||||||
// value will look like 00ae29001403e2290013001629001201
|
// value will look like 00ae29001403e2290013001629001201
|
||||||
// breaking this apart and swapping byte order where appropriate, this breaks down to:
|
// breaking this apart and swapping byte order where appropriate, this breaks down to:
|
||||||
@@ -213,36 +169,22 @@ 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.clusterInt == 0xFC02 && descMap.attrInt == 0x0012 && descMap.value.size() == 24) {
|
||||||
else if (descMap.cluster == "FC02" && descMap.attrId == "0012" && descMap.value.size() == 24) {
|
|
||||||
// The size is checked to ensure the attribute report contains X, Y and Z values
|
// 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
|
// If all three axis are not included then the attribute report is ignored
|
||||||
result << parseAxis(descMap.value)
|
result = parseAxis(descMap.value)
|
||||||
}
|
}
|
||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
|
||||||
result << getBatteryResult(Integer.parseInt(descMap.value, 16))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (description?.startsWith('temperature: ')) {
|
|
||||||
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseIasMessage(String description) {
|
private Map parseIasMessage(String description) {
|
||||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||||
Map resultMap = [:]
|
Map resultMap = [:]
|
||||||
|
|
||||||
if (garageSensor != "Yes"){
|
if (garageSensor != "Yes") {
|
||||||
resultMap = zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed')
|
resultMap = zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed')
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultMap
|
return resultMap
|
||||||
}
|
}
|
||||||
@@ -254,31 +196,19 @@ def updated() {
|
|||||||
def descriptionText = "Updating device to garage sensor"
|
def descriptionText = "Updating device to garage sensor"
|
||||||
if (device.latestValue("status") == "open") {
|
if (device.latestValue("status") == "open") {
|
||||||
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true)
|
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true)
|
||||||
}
|
} else if (device.latestValue("status") == "closed") {
|
||||||
else if (device.latestValue("status") == "closed") {
|
|
||||||
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true)
|
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
def descriptionText = "Updating device to open/close sensor"
|
def descriptionText = "Updating device to open/close sensor"
|
||||||
if (device.latestValue("status") == "garage-open") {
|
if (device.latestValue("status") == "garage-open") {
|
||||||
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true)
|
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true)
|
||||||
}
|
} else if (device.latestValue("status") == "garage-closed") {
|
||||||
else if (device.latestValue("status") == "garage-closed") {
|
|
||||||
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true)
|
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
|
||||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
|
||||||
if(getTemperatureScale() == "C"){
|
|
||||||
return Math.round(celsius)
|
|
||||||
} else {
|
|
||||||
return Math.round(celsiusToFahrenheit(celsius))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getBatteryResult(rawValue) {
|
private Map getBatteryResult(rawValue) {
|
||||||
log.debug "Battery rawValue = ${rawValue}"
|
log.debug "Battery rawValue = ${rawValue}"
|
||||||
|
|
||||||
@@ -317,25 +247,6 @@ private Map getBatteryResult(rawValue) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getTemperatureResult(value) {
|
|
||||||
log.debug "Temperature"
|
|
||||||
if (tempOffset) {
|
|
||||||
def offset = tempOffset as int
|
|
||||||
def v = value as int
|
|
||||||
value = v + offset
|
|
||||||
}
|
|
||||||
def descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C':
|
|
||||||
'{{ device.displayName }} was {{ value }}°F'
|
|
||||||
|
|
||||||
return [
|
|
||||||
name: 'temperature',
|
|
||||||
value: value,
|
|
||||||
descriptionText: descriptionText,
|
|
||||||
translatable: true,
|
|
||||||
unit: temperatureScale
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getContactResult(value) {
|
private Map getContactResult(value) {
|
||||||
log.debug "Contact: ${device.displayName} value = ${value}"
|
log.debug "Contact: ${device.displayName} value = ${value}"
|
||||||
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
|
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
|
||||||
@@ -346,24 +257,24 @@ private Map getContactResult(value) {
|
|||||||
private getAccelerationResult(numValue) {
|
private getAccelerationResult(numValue) {
|
||||||
log.debug "Acceleration"
|
log.debug "Acceleration"
|
||||||
def name = "acceleration"
|
def name = "acceleration"
|
||||||
def value
|
def value
|
||||||
def descriptionText
|
def descriptionText
|
||||||
|
|
||||||
if ( numValue.endsWith("1") ) {
|
if (numValue.endsWith("1")) {
|
||||||
value = "active"
|
value = "active"
|
||||||
descriptionText = '{{ device.displayName }} was active'
|
descriptionText = '{{ device.displayName }} was active'
|
||||||
} else {
|
} else {
|
||||||
value = "inactive"
|
value = "inactive"
|
||||||
descriptionText = '{{ device.displayName }} was inactive'
|
descriptionText = '{{ device.displayName }} was inactive'
|
||||||
}
|
}
|
||||||
|
|
||||||
def isStateChange = isStateChange(device, name, value)
|
def isStateChange = isStateChange(device, name, value)
|
||||||
return [
|
return [
|
||||||
name: name,
|
name : name,
|
||||||
value: value,
|
value : value,
|
||||||
descriptionText: descriptionText,
|
descriptionText: descriptionText,
|
||||||
isStateChange: isStateChange,
|
isStateChange : isStateChange,
|
||||||
translatable: true
|
translatable : true
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,27 +288,12 @@ def ping() {
|
|||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "Refreshing Values "
|
log.debug "Refreshing Values "
|
||||||
|
|
||||||
def refreshCmds = []
|
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||||
|
|
||||||
if (device.getDataValue("manufacturer") == "SmartThings") {
|
|
||||||
log.debug "Refreshing Values for manufacturer: SmartThings "
|
|
||||||
/* These values of Motion Threshold Multiplier(0x01) and Motion Threshold (0x0276)
|
|
||||||
seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
|
|
||||||
Separating these out in a separate if-else because I do not want to touch Centralite part
|
|
||||||
as of now.
|
|
||||||
*/
|
|
||||||
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x01, [mfgCode: manufacturerCode])
|
|
||||||
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0002, 0x21, 0x0276, [mfgCode: manufacturerCode])
|
|
||||||
} else {
|
|
||||||
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x02, [mfgCode: manufacturerCode])
|
|
||||||
}
|
|
||||||
|
|
||||||
//Common refresh commands
|
|
||||||
refreshCmds += zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
|
||||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
||||||
zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode])
|
zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode]) +
|
||||||
|
zigbee.enrollResponse()
|
||||||
|
|
||||||
return refreshCmds + zigbee.enrollResponse()
|
return refreshCmds
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
@@ -406,10 +302,27 @@ def configure() {
|
|||||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
log.debug "Configuring Reporting"
|
log.debug "Configuring Reporting"
|
||||||
|
def configCmds = []
|
||||||
|
|
||||||
|
if (device.getDataValue("manufacturer") == "SmartThings") {
|
||||||
|
log.debug "Refreshing Values for manufacturer: SmartThings "
|
||||||
|
/* These values of Motion Threshold Multiplier(0x01) and Motion Threshold (0x0276)
|
||||||
|
seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
|
||||||
|
Separating these out in a separate if-else because I do not want to touch Centralite part
|
||||||
|
as of now.
|
||||||
|
*/
|
||||||
|
configCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x01, [mfgCode: manufacturerCode])
|
||||||
|
configCmds += zigbee.writeAttribute(0xFC02, 0x0002, 0x21, 0x0276, [mfgCode: manufacturerCode])
|
||||||
|
} else {
|
||||||
|
// Write a motion threshold of 2 * .063g = .126g
|
||||||
|
// Currently due to a Centralite firmware issue, this will cause a read attribute response that
|
||||||
|
// indicates acceleration even when there isn't.
|
||||||
|
configCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x02, [mfgCode: manufacturerCode])
|
||||||
|
}
|
||||||
|
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||||
def configCmds = zigbee.batteryConfig() +
|
configCmds += zigbee.batteryConfig() +
|
||||||
zigbee.temperatureConfig(30, 300) +
|
zigbee.temperatureConfig(30, 300) +
|
||||||
zigbee.configureReporting(0xFC02, 0x0010, DataType.BITMAP8, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0010, DataType.BITMAP8, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
||||||
zigbee.configureReporting(0xFC02, 0x0012, DataType.INT16, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0012, DataType.INT16, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||||
@@ -459,17 +372,16 @@ def garageEvent(zValue) {
|
|||||||
def absValue = zValue.abs()
|
def absValue = zValue.abs()
|
||||||
def contactValue = null
|
def contactValue = null
|
||||||
def garageValue = null
|
def garageValue = null
|
||||||
if (absValue>900) {
|
if (absValue > 900) {
|
||||||
contactValue = 'closed'
|
contactValue = 'closed'
|
||||||
garageValue = 'garage-closed'
|
garageValue = 'garage-closed'
|
||||||
}
|
} else if (absValue < 100) {
|
||||||
else if (absValue < 100) {
|
|
||||||
contactValue = 'open'
|
contactValue = 'open'
|
||||||
garageValue = 'garage-open'
|
garageValue = 'garage-open'
|
||||||
}
|
}
|
||||||
if (contactValue != null){
|
if (contactValue != null) {
|
||||||
def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' :'{{ device.displayName }} was closed'
|
def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
|
||||||
sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed:false, translatable: true)
|
sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed: false, translatable: true)
|
||||||
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true)
|
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -482,14 +394,14 @@ private Map getXyzResult(results, description) {
|
|||||||
def isStateChange = isStateChange(device, name, value)
|
def isStateChange = isStateChange(device, name, value)
|
||||||
|
|
||||||
[
|
[
|
||||||
name: name,
|
name : name,
|
||||||
value: value,
|
value : value,
|
||||||
unit: null,
|
unit : null,
|
||||||
linkText: linkText,
|
linkText : linkText,
|
||||||
descriptionText: descriptionText,
|
descriptionText: descriptionText,
|
||||||
handlerName: name,
|
handlerName : name,
|
||||||
isStateChange: isStateChange,
|
isStateChange : isStateChange,
|
||||||
displayed: false
|
displayed : false
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,25 +416,3 @@ private getManufacturerCode() {
|
|||||||
private hexToInt(value) {
|
private hexToInt(value) {
|
||||||
new BigInteger(value, 16)
|
new BigInteger(value, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
private hex(value) {
|
|
||||||
new BigInteger(Math.round(value).toString()).toString(16)
|
|
||||||
}
|
|
||||||
|
|
||||||
private String swapEndianHex(String hex) {
|
|
||||||
reverseArray(hex.decodeHex()).encodeHex()
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] reverseArray(byte[] array) {
|
|
||||||
int i = 0;
|
|
||||||
int j = array.length - 1;
|
|
||||||
byte tmp;
|
|
||||||
while (j > i) {
|
|
||||||
tmp = array[j];
|
|
||||||
array[j] = array[i];
|
|
||||||
array[i] = tmp;
|
|
||||||
j--;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings") {
|
definition(name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Contact Sensor"
|
capability "Contact Sensor"
|
||||||
@@ -43,155 +43,77 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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") {
|
||||||
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
|
attributeState "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
|
||||||
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
|
attributeState "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"],
|
||||||
[value: 59, color: "#90d2a7"],
|
[value: 59, color: "#90d2a7"],
|
||||||
[value: 74, color: "#44b621"],
|
[value: 74, color: "#44b621"],
|
||||||
[value: 84, color: "#f1d801"],
|
[value: 84, color: "#f1d801"],
|
||||||
[value: 95, color: "#d04e00"],
|
[value: 95, color: "#d04e00"],
|
||||||
[value: 96, color: "#bc2323"]
|
[value: 96, color: "#bc2323"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
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 (["contact", "temperature"])
|
main(["contact", "temperature"])
|
||||||
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 = zigbee.getEvent(description)
|
||||||
if (description?.startsWith('catchall:')) {
|
if (!map) {
|
||||||
map = parseCatchAllMessage(description)
|
if (description?.startsWith('zone status')) {
|
||||||
|
map = parseIasMessage(description)
|
||||||
|
} else {
|
||||||
|
Map descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
|
if (descMap?.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) {
|
||||||
|
map = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
|
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
|
||||||
|
if (descMap.data[0] == "00") {
|
||||||
|
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
} else {
|
||||||
|
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (description?.startsWith('read attr -')) {
|
|
||||||
map = parseReportAttributeMessage(description)
|
|
||||||
}
|
|
||||||
else if (description?.startsWith('temperature: ')) {
|
|
||||||
map = parseCustomMessage(description)
|
|
||||||
}
|
|
||||||
else if (description?.startsWith('zone status')) {
|
|
||||||
map = parseIasMessage(description)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : [:]
|
def result = map ? createEvent(map) : [:]
|
||||||
|
|
||||||
if (description?.startsWith('enroll request')) {
|
if (description?.startsWith('enroll request')) {
|
||||||
List cmds = zigbee.enrollResponse()
|
List cmds = zigbee.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
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
def cluster = zigbee.parse(description)
|
|
||||||
if (shouldProcessMessage(cluster)) {
|
|
||||||
switch(cluster.clusterId) {
|
|
||||||
case 0x0001:
|
|
||||||
// 0x07 - configure reporting
|
|
||||||
if (cluster.command != 0x07) {
|
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0x0402:
|
|
||||||
if (cluster.command == 0x07){
|
|
||||||
if (cluster.data[0] == 0x00) {
|
|
||||||
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
|
|
||||||
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// temp is last 2 data values. reverse to swap endian
|
|
||||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
|
||||||
def value = getTemperature(temp)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
|
||||||
// 0x0B is default response indicating message got through
|
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
|
||||||
cluster.command == 0x0B ||
|
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
|
||||||
return !ignoredMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getHumidity(value) {
|
|
||||||
return Math.round(Double.parseDouble(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseReportAttributeMessage(String description) {
|
|
||||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
|
||||||
def nameAndValue = param.split(":")
|
|
||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
|
||||||
}
|
}
|
||||||
log.debug "Desc Map: $descMap"
|
return result
|
||||||
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
|
||||||
def value = getTemperature(descMap.value)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
|
||||||
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (description?.startsWith('temperature: ')) {
|
|
||||||
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseIasMessage(String description) {
|
private Map parseIasMessage(String description) {
|
||||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||||
return zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed')
|
return zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed')
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTemperature(value) {
|
|
||||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
|
||||||
if(getTemperatureScale() == "C"){
|
|
||||||
return celsius
|
|
||||||
} else {
|
|
||||||
return celsiusToFahrenheit(celsius) as Integer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getBatteryResult(rawValue) {
|
private Map getBatteryResult(rawValue) {
|
||||||
log.debug 'Battery'
|
log.debug 'Battery'
|
||||||
def linkText = getLinkText(device)
|
def linkText = getLinkText(device)
|
||||||
@@ -204,8 +126,8 @@ private Map getBatteryResult(rawValue) {
|
|||||||
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)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
if (roundedPct <= 0)
|
||||||
roundedPct = 1
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
result.name = 'battery'
|
result.name = 'battery'
|
||||||
@@ -214,31 +136,14 @@ private Map getBatteryResult(rawValue) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getTemperatureResult(value) {
|
|
||||||
log.debug 'TEMP'
|
|
||||||
def linkText = getLinkText(device)
|
|
||||||
if (tempOffset) {
|
|
||||||
def offset = tempOffset as int
|
|
||||||
def v = value as int
|
|
||||||
value = v + offset
|
|
||||||
}
|
|
||||||
def descriptionText = "${linkText} was ${value}°${temperatureScale}"
|
|
||||||
return [
|
|
||||||
name: 'temperature',
|
|
||||||
value: value,
|
|
||||||
descriptionText: descriptionText,
|
|
||||||
unit: temperatureScale
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getContactResult(value) {
|
private Map getContactResult(value) {
|
||||||
log.debug 'Contact Status'
|
log.debug 'Contact Status'
|
||||||
def linkText = getLinkText(device)
|
def linkText = getLinkText(device)
|
||||||
def descriptionText = "${linkText} was ${value == 'open' ? 'opened' : 'closed'}"
|
def descriptionText = "${linkText} was ${value == 'open' ? 'opened' : 'closed'}"
|
||||||
return [
|
return [
|
||||||
name: 'contact',
|
name : 'contact',
|
||||||
value: value,
|
value : value,
|
||||||
descriptionText: descriptionText
|
descriptionText: descriptionText
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +157,7 @@ def ping() {
|
|||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "Refreshing Temperature and Battery"
|
log.debug "Refreshing Temperature and Battery"
|
||||||
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
||||||
|
|
||||||
return refreshCmds + zigbee.enrollResponse()
|
return refreshCmds + zigbee.enrollResponse()
|
||||||
}
|
}
|
||||||
@@ -266,5 +171,5 @@ def configure() {
|
|||||||
|
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||||
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
|
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import physicalgraph.zigbee.zcl.DataType
|
import physicalgraph.zigbee.zcl.DataType
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "SmartSense Temp/Humidity Sensor",namespace: "smartthings", author: "SmartThings") {
|
definition(name: "SmartSense Temp/Humidity Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
@@ -33,7 +33,7 @@ metadata {
|
|||||||
status 'H 45': 'catchall: 0104 FC45 01 01 0140 00 D9B9 00 04 C2DF 0A 01 0000218911'
|
status 'H 45': 'catchall: 0104 FC45 01 01 0140 00 D9B9 00 04 C2DF 0A 01 0000218911'
|
||||||
status 'H 57': 'catchall: 0104 FC45 01 01 0140 00 4E55 00 04 C2DF 0A 01 0000211316'
|
status 'H 57': 'catchall: 0104 FC45 01 01 0140 00 4E55 00 04 C2DF 0A 01 0000211316'
|
||||||
status 'H 53': 'catchall: 0104 FC45 01 01 0140 00 20CD 00 04 C2DF 0A 01 0000219814'
|
status 'H 53': 'catchall: 0104 FC45 01 01 0140 00 20CD 00 04 C2DF 0A 01 0000219814'
|
||||||
status 'H 43': 'read attr - raw: BF7601FC450C00000021A410, dni: BF76, endpoint: 01, cluster: FC45, size: 0C, attrId: 0000, result: success, encoding: 21, value: 10a4'
|
status 'H 43': 'read attr - raw: BF7601FC450C00000021A410, dni: BF76, endpoint: 01, cluster: FC45, size: 0C, attrId: 0000, result: success, encoding: 21, value: 10a4'
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences {
|
preferences {
|
||||||
@@ -42,28 +42,28 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"temperature", type: "generic", width: 6, height: 4){
|
multiAttributeTile(name: "temperature", type: "generic", width: 6, height: 4) {
|
||||||
tileAttribute ("device.temperature", key: "PRIMARY_CONTROL") {
|
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||||
attributeState "temperature", label:'${currentValue}°',
|
attributeState "temperature", label: '${currentValue}°',
|
||||||
backgroundColors:[
|
backgroundColors: [
|
||||||
[value: 31, color: "#153591"],
|
[value: 31, color: "#153591"],
|
||||||
[value: 44, color: "#1e9cbb"],
|
[value: 44, color: "#1e9cbb"],
|
||||||
[value: 59, color: "#90d2a7"],
|
[value: 59, color: "#90d2a7"],
|
||||||
[value: 74, color: "#44b621"],
|
[value: 74, color: "#44b621"],
|
||||||
[value: 84, color: "#f1d801"],
|
[value: 84, color: "#f1d801"],
|
||||||
[value: 95, color: "#d04e00"],
|
[value: 95, color: "#d04e00"],
|
||||||
[value: 96, color: "#bc2323"]
|
[value: 96, color: "#bc2323"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
|
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
|
||||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
state "humidity", label: '${currentValue}% humidity', unit: ""
|
||||||
}
|
}
|
||||||
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'
|
state "battery", label: '${currentValue}% battery'
|
||||||
}
|
}
|
||||||
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 "temperature", "humidity"
|
main "temperature", "humidity"
|
||||||
@@ -74,142 +74,31 @@ metadata {
|
|||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "description: $description"
|
log.debug "description: $description"
|
||||||
|
|
||||||
Map map = [:]
|
// getEvent will handle temperature and humidity
|
||||||
if (description?.startsWith('catchall:')) {
|
Map map = zigbee.getEvent(description)
|
||||||
map = parseCatchAllMessage(description)
|
if (!map) {
|
||||||
}
|
Map descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
else if (description?.startsWith('read attr -')) {
|
if (descMap.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) {
|
||||||
map = parseReportAttributeMessage(description)
|
map = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
}
|
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
|
||||||
else if (description?.startsWith('temperature: ') || description?.startsWith('humidity: ')) {
|
if (descMap.data[0] == "00") {
|
||||||
map = parseCustomMessage(description)
|
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
} else {
|
||||||
|
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
return map ? createEvent(map) : [:]
|
return map ? createEvent(map) : [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseCatchAllMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
def cluster = zigbee.parse(description)
|
|
||||||
if (shouldProcessMessage(cluster)) {
|
|
||||||
switch(cluster.clusterId) {
|
|
||||||
case 0x0001:
|
|
||||||
// 0x07 - configure reporting
|
|
||||||
if (cluster.command != 0x07) {
|
|
||||||
resultMap = getBatteryResult(cluster.data.last())
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0x0402:
|
|
||||||
if (cluster.command == 0x07) {
|
|
||||||
if (cluster.data[0] == 0x00){
|
|
||||||
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
|
|
||||||
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// temp is last 2 data values. reverse to swap endian
|
|
||||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
|
||||||
def value = getTemperature(temp)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 0xFC45:
|
|
||||||
// 0x07 - configure reporting
|
|
||||||
if (cluster.command != 0x07) {
|
|
||||||
String pctStr = cluster.data[-1, -2].collect { Integer.toHexString(it) }.join('')
|
|
||||||
String display = Math.round(Integer.valueOf(pctStr, 16) / 100)
|
|
||||||
resultMap = getHumidityResult(display)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldProcessMessage(cluster) {
|
|
||||||
// 0x0B is default response indicating message got through
|
|
||||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
|
||||||
cluster.command == 0x0B ||
|
|
||||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
|
||||||
return !ignoredMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseReportAttributeMessage(String description) {
|
|
||||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
|
||||||
def nameAndValue = param.split(":")
|
|
||||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
|
||||||
}
|
|
||||||
log.debug "Desc Map: $descMap"
|
|
||||||
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
|
||||||
def value = getTemperature(descMap.value)
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
|
||||||
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
|
||||||
}
|
|
||||||
else if (descMap.cluster == "FC45" && descMap.attrId == "0000") {
|
|
||||||
def value = getReportAttributeHumidity(descMap.value)
|
|
||||||
resultMap = getHumidityResult(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
def getReportAttributeHumidity(String value) {
|
|
||||||
def humidity = null
|
|
||||||
if (value?.trim()) {
|
|
||||||
try {
|
|
||||||
// value is hex with no decimal
|
|
||||||
def pct = Integer.parseInt(value.trim(), 16) / 100
|
|
||||||
humidity = String.format('%.0f', pct)
|
|
||||||
} catch(NumberFormatException nfe) {
|
|
||||||
log.debug "Error converting $value to humidity"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return humidity
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map parseCustomMessage(String description) {
|
|
||||||
Map resultMap = [:]
|
|
||||||
if (description?.startsWith('temperature: ')) {
|
|
||||||
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
|
||||||
resultMap = getTemperatureResult(value)
|
|
||||||
}
|
|
||||||
else if (description?.startsWith('humidity: ')) {
|
|
||||||
def pct = (description - "humidity: " - "%").trim()
|
|
||||||
if (pct.isNumber()) {
|
|
||||||
def value = Math.round(new BigDecimal(pct)).toString()
|
|
||||||
resultMap = getHumidityResult(value)
|
|
||||||
} else {
|
|
||||||
log.error "invalid humidity: ${pct}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
def getTemperature(value) {
|
|
||||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
|
||||||
if(getTemperatureScale() == "C"){
|
|
||||||
return celsius
|
|
||||||
} else {
|
|
||||||
return celsiusToFahrenheit(celsius) as Integer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = [:]
|
||||||
|
|
||||||
def volts = rawValue / 10
|
def volts = rawValue / 10
|
||||||
if (!(rawValue == 0 || rawValue == 255)) {
|
if (!(rawValue == 0 || rawValue == 255)) {
|
||||||
@@ -228,41 +117,22 @@ private Map getBatteryResult(rawValue) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getTemperatureResult(value) {
|
|
||||||
log.debug 'TEMP'
|
|
||||||
def linkText = getLinkText(device)
|
|
||||||
if (tempOffset) {
|
|
||||||
def offset = tempOffset as int
|
|
||||||
def v = value as int
|
|
||||||
value = v + offset
|
|
||||||
}
|
|
||||||
def descriptionText = "${linkText} was ${value}°${temperatureScale}"
|
|
||||||
return [
|
|
||||||
name: 'temperature',
|
|
||||||
value: value,
|
|
||||||
descriptionText: descriptionText,
|
|
||||||
unit: temperatureScale
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map getHumidityResult(value) {
|
|
||||||
log.debug 'Humidity'
|
|
||||||
return value ? [name: 'humidity', value: value, unit: '%'] : [:]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) // Read the Battery Level
|
return zigbee.readAttribute(0x0001, 0x0020) // Read the Battery Level
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh()
|
def refresh() {
|
||||||
{
|
|
||||||
log.debug "refresh temperature, humidity, and battery"
|
log.debug "refresh temperature, humidity, and battery"
|
||||||
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware
|
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware
|
||||||
|
zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware
|
||||||
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
||||||
|
zigbee.configureReporting(0xFC45, 0x0000, DataType.INT16, 30, 3600, 100) +
|
||||||
|
zigbee.batteryConfig() +
|
||||||
|
zigbee.temperatureConfig(30, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
@@ -271,10 +141,8 @@ def configure() {
|
|||||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
def humidityConfigCmds = zigbee.configureReporting(0xFC45, 0x0000, DataType.INT16, 30, 3600, 0x0064)
|
|
||||||
|
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||||
return refresh() + humidityConfigCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
|
return refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user