Compare commits

..

1 Commits

Author SHA1 Message Date
박혜진
b7f4d9e1d3 MSA-991: test 2016-03-23 02:41:50 -05:00
18 changed files with 329 additions and 365 deletions

View File

@@ -7,10 +7,9 @@ apply plugin: 'smartthings-hipchat'
buildscript { buildscript {
dependencies { dependencies {
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.6" classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.3"
} }
repositories { repositories {
mavenLocal()
jcenter() jcenter()
maven { maven {
credentials { credentials {

View File

@@ -15,13 +15,13 @@ deployment:
develop: develop:
branch: master branch: master
commands: commands:
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3Buckets="$S3_BUCKETS_DEV" - ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3BucketName=$S3_BUCKET_NAME_PREPROD_DEV
- ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH - ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH
- ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD - ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD
stage: stage:
branch: staging branch: staging
commands: commands:
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3Buckets="$S3_BUCKETS_STAGE" - ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3BucketName=$S3_BUCKET_NAME_PREPROD_STAGING
- ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH - ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH
- ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD - ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD

View File

@@ -103,104 +103,62 @@ void nextLevel() {
} }
void setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
if (verifyPercent(percent)) { parent.setLevel(this, percent)
parent.setLevel(this, percent) sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
}
} }
void setSaturation(percent) { void setSaturation(percent) {
log.debug "Executing 'setSaturation'" log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) { parent.setSaturation(this, percent)
parent.setSaturation(this, percent) sendEvent(name: "saturation", value: percent, displayed: false)
sendEvent(name: "saturation", value: percent, displayed: false)
}
} }
void setHue(percent) { void setHue(percent) {
log.debug "Executing 'setHue'" log.debug "Executing 'setHue'"
if (verifyPercent(percent)) { parent.setHue(this, percent)
parent.setHue(this, percent) sendEvent(name: "hue", value: percent, displayed: false)
sendEvent(name: "hue", value: percent, displayed: false)
}
} }
void setColor(value) { void setColor(value) {
log.debug "setColor: ${value}, $this" log.debug "setColor: ${value}, $this"
def events = [] parent.setColor(this, value)
def validValues = [:] if (value.hue) { sendEvent(name: "hue", value: value.hue, displayed: false)}
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation, displayed: false)}
if (verifyPercent(value.hue)) { if (value.hex) { sendEvent(name: "color", value: value.hex)}
events << createEvent(name: "hue", value: value.hue, displayed: false) if (value.level) { sendEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")}
validValues.hue = value.hue sendEvent(name: "switch", value: "on")
}
if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off"
} else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on"
}
if (!events.isEmpty()) {
parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
}
} }
void reset() { void reset() {
log.debug "Executing 'reset'" log.debug "Executing 'reset'"
def value = [level:100, saturation:56, hue:23] def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value) setAdjustedColor(value)
parent.poll() parent.poll()
} }
void setAdjustedColor(value) { void setAdjustedColor(value) {
if (value) { if (value) {
log.trace "setAdjustedColor: ${value}" log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:] def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue) adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100 // Needed because color picker always sends 100
adjusted.level = null adjusted.level = null
setColor(adjusted) setColor(adjusted)
} else {
log.warn "Invalid color input"
} }
} }
void setColorTemperature(value) { void setColorTemperature(value) {
if (value) { if (value) {
log.trace "setColorTemperature: ${value}k" log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value) parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value) sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on") }
} else {
log.warn "Invalid color temperature"
}
} }
void refresh() { void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent.manualRefresh() parent.manualRefresh()
} }
def adjustOutgoingHue(percent) { def adjustOutgoingHue(percent) {
@@ -219,14 +177,3 @@ def adjustOutgoingHue(percent) {
log.info "percent: $percent, adjusted: $adjusted" log.info "percent: $percent, adjusted: $adjusted"
adjusted adjusted
} }
def verifyPercent(percent) {
if (percent == null)
return false
else if (percent >= 0 && percent <= 100) {
return true
} else {
log.warn "$percent is not 0-100"
return false
}
}

View File

@@ -79,12 +79,8 @@ void off() {
void setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) { parent.setLevel(this, percent)
parent.setLevel(this, percent) sendEvent(name: "level", value: percent)
sendEvent(name: "level", value: percent)
} else {
log.warn "$percent is not 0-100"
}
} }
void refresh() { void refresh() {

View File

@@ -28,8 +28,4 @@
'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다. '''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다.
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다. '''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다.
'''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전원은 {{ value }}와트입니다 '''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전원은 {{ value }}와트입니다
'''On'''.ko=켜기
'''Off'''.ko=끄기
'''Turning On'''.ko=켜기
'''Turning Off'''.ko=끄기
#============================================================================== #==============================================================================

View File

@@ -65,10 +65,10 @@ metadata {
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: '${name}', 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: '${name}', 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: '${name}', 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: '${name}', 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'

View File

@@ -43,7 +43,3 @@
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다. '''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
'''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중 '''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중
'''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중 '''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중
'''Inactive'''.ko=비활성
'''Active'''.ko=활성
'''Open'''.ko=열다
'''Closed'''.ko=닫은

View File

@@ -83,19 +83,19 @@ 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:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
attributeState "closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821" attributeState "closed", label:'${name}', 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:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
state("closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821") state("closed", label:'${name}', 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:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
state("inactive", label:'Inactive', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff") state("inactive", label:'${name}', 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}°',

View File

@@ -203,7 +203,7 @@ private List parseContactMessage(String description) {
parts.each { part -> parts.each { part ->
part = part.trim() part = part.trim()
if (part.startsWith('contactState:')) { if (part.startsWith('contactState:')) {
results.addAll(getContactResult(part, description)) results << getContactResult(part, description)
} }
else if (part.startsWith('accelerationState:')) { else if (part.startsWith('accelerationState:')) {
results << getAccelerationResult(part, description) results << getAccelerationResult(part, description)
@@ -316,7 +316,7 @@ private List getContactResult(part, description) {
results results
} }
private Map getAccelerationResult(part, description) { private getAccelerationResult(part, description) {
def name = "acceleration" def name = "acceleration"
def value = part.endsWith("1") ? "active" : "inactive" def value = part.endsWith("1") ? "active" : "inactive"
def linkText = getLinkText(device) def linkText = getLinkText(device)
@@ -335,7 +335,7 @@ private Map getAccelerationResult(part, description) {
] ]
} }
private Map getTempResult(part, description) { private getTempResult(part, description) {
def name = "temperature" def name = "temperature"
def temperatureScale = getTemperatureScale() def temperatureScale = getTemperatureScale()
def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale) def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale)
@@ -360,7 +360,7 @@ private Map getTempResult(part, description) {
] ]
} }
private Map getXyzResult(results, description) { private getXyzResult(results, description) {
def name = "threeAxis" def name = "threeAxis"
def value = "${results.x},${results.y},${results.z}" def value = "${results.x},${results.y},${results.z}"
def linkText = getLinkText(device) def linkText = getLinkText(device)
@@ -379,7 +379,7 @@ private Map getXyzResult(results, description) {
] ]
} }
private Map getBatteryResult(part, description) { private getBatteryResult(part, description) {
def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null
def name = "battery" def name = "battery"
def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor) def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor)
@@ -400,7 +400,7 @@ private Map getBatteryResult(part, description) {
] ]
} }
private Map getRssiResult(part, description, lastHop=false) { private getRssiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopRssi" : "rssi" def name = lastHop ? "lastHopRssi" : "rssi"
def valueString = part.split(":")[1].trim() def valueString = part.split(":")[1].trim()
def value = (Integer.parseInt(valueString) - 128).toString() def value = (Integer.parseInt(valueString) - 128).toString()
@@ -431,7 +431,7 @@ private Map getRssiResult(part, description, lastHop=false) {
* Note: To make the signal strength indicator more accurate, we could combine * Note: To make the signal strength indicator more accurate, we could combine
* LQI with RSSI. * LQI with RSSI.
*/ */
private Map getLqiResult(part, description, lastHop=false) { private getLqiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopLqi" : "lqi" def name = lastHop ? "lastHopLqi" : "lqi"
def valueString = part.split(":")[1].trim() def valueString = part.split(":")[1].trim()
def percentageOf = 255 def percentageOf = 255

View File

@@ -56,17 +56,21 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description) def resultMap = zigbee.getKnownDescription(description)
if (event) { if (resultMap) {
log.info event log.info resultMap
if (event.name == "power") { if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else if (resultMap.type == "power") {
def powerValue
if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power
event.value = (event.value as Integer) / 10 //TODO: The divisor value needs to be set as part of configuration powerValue = (resultMap.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(event) sendEvent(name: "power", value: powerValue)
} }
} }
else { else {
sendEvent(event) sendEvent(name: resultMap.type, value: resultMap.value)
} }
} }
else { else {

View File

@@ -51,9 +51,15 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description) def resultMap = zigbee.getKnownDescription(description)
if (event) { if (resultMap) {
sendEvent(event) log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else {
sendEvent(name: resultMap.type, value: resultMap.value)
}
} }
else { else {
log.warn "DID NOT PARSE MESSAGE for description : $description" log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -83,19 +83,32 @@ def uninstalled() {
} }
def configure() { def configure() {
/*
def cmds = def cmds =
zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE, zigbee.configSetup("${CLUSTER_DOORLOCK}", "${DOORLOCK_ATTR_LOCKSTATE}",
TYPE_ENUM8, 0, 3600, null) + "${TYPE_ENUM8}", 0, 3600, "{01}") +
zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING, zigbee.configSetup("${CLUSTER_POWER}", "${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING}",
TYPE_U8, 600, 21600, 0x01) "${TYPE_U8}", 600, 21600, "{01}")
*/
def zigbeeId = device.zigbeeId
def cmds =
[
"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 1 ${CLUSTER_DOORLOCK} {$zigbeeId} {}", "delay 200",
"zcl global send-me-a-report ${CLUSTER_DOORLOCK} ${DOORLOCK_ATTR_LOCKSTATE} ${TYPE_ENUM8} 0 3600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 0x${device.endpointId}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 1 ${CLUSTER_POWER} {$zigbeeId} {}", "delay 200",
"zcl global send-me-a-report ${CLUSTER_POWER} ${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING} ${TYPE_U8} 600 21600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 0x${device.endpointId}", "delay 200",
]
log.info "configure() --- cmds: $cmds" log.info "configure() --- cmds: $cmds"
return cmds + refresh() // send refresh cmds as part of config return cmds + refresh() // send refresh cmds as part of config
} }
def refresh() { def refresh() {
def cmds = def cmds =
zigbee.readAttribute(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE) + zigbee.refreshData("${CLUSTER_DOORLOCK}", "${DOORLOCK_ATTR_LOCKSTATE}") +
zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) zigbee.refreshData("${CLUSTER_POWER}", "${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING}")
log.info "refresh() --- cmds: $cmds" log.info "refresh() --- cmds: $cmds"
return cmds return cmds
} }
@@ -108,27 +121,34 @@ def parse(String description) {
map = parseReportAttributeMessage(description) map = parseReportAttributeMessage(description)
} }
log.debug "parse() --- Parse returned $map"
def result = map ? createEvent(map) : null def result = map ? createEvent(map) : null
log.debug "parse() --- returned: $result"
return result return result
} }
// Lock capability commands // Lock capability commands
def lock() { def lock() {
def cmds = zigbee.command(CLUSTER_DOORLOCK, DOORLOCK_CMD_LOCK_DOOR) //def cmds = zigbee.zigbeeCommand("${CLUSTER_DOORLOCK}", "${DOORLOCK_CMD_LOCK_DOOR}", "{}")
log.info "lock() -- cmds: $cmds" //log.info "lock() -- cmds: $cmds"
return cmds //return cmds
"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} ${CLUSTER_DOORLOCK} ${DOORLOCK_CMD_LOCK_DOOR} {}"
} }
def unlock() { def unlock() {
def cmds = zigbee.command(CLUSTER_DOORLOCK, DOORLOCK_CMD_UNLOCK_DOOR) //def cmds = zigbee.zigbeeCommand("${CLUSTER_DOORLOCK}", "${DOORLOCK_CMD_UNLOCK_DOOR}", "{}")
log.info "unlock() -- cmds: $cmds" //log.info "unlock() -- cmds: $cmds"
return cmds //return cmds
"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} ${CLUSTER_DOORLOCK} ${DOORLOCK_CMD_UNLOCK_DOOR} {}"
} }
// Private methods // Private methods
private Map parseReportAttributeMessage(String description) { private Map parseReportAttributeMessage(String description) {
log.trace "parseReportAttributeMessage() --- description: $description"
Map descMap = zigbee.parseDescriptionAsMap(description) Map descMap = zigbee.parseDescriptionAsMap(description)
log.debug "parseReportAttributeMessage() --- descMap: $descMap"
Map resultMap = [:] Map resultMap = [:]
if (descMap.clusterInt == CLUSTER_POWER && descMap.attrInt == POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) { if (descMap.clusterInt == CLUSTER_POWER && descMap.attrInt == POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) {
resultMap.name = "battery" resultMap.name = "battery"
@@ -136,24 +156,18 @@ private Map parseReportAttributeMessage(String description) {
if (device.getDataValue("manufacturer") == "Yale") { //Handling issue with Yale locks incorrect battery reporting if (device.getDataValue("manufacturer") == "Yale") { //Handling issue with Yale locks incorrect battery reporting
resultMap.value = Integer.parseInt(descMap.value, 16) resultMap.value = Integer.parseInt(descMap.value, 16)
} }
log.info "parseReportAttributeMessage() --- battery: ${resultMap.value}"
} }
else if (descMap.clusterInt == CLUSTER_DOORLOCK && descMap.attrInt == DOORLOCK_ATTR_LOCKSTATE) { else if (descMap.clusterInt == CLUSTER_DOORLOCK && descMap.attrInt == DOORLOCK_ATTR_LOCKSTATE) {
def value = Integer.parseInt(descMap.value, 16) def value = Integer.parseInt(descMap.value, 16)
def linkText = getLinkText(device)
resultMap.name = "lock" resultMap.name = "lock"
if (value == 0) { resultMap.putAll([0:["value":"unknown",
resultMap.value = "unknown" "descriptionText":"Not fully locked"],
resultMap.descriptionText = "${linkText} is not fully locked" 1:["value":"locked"],
} else if (value == 1) { 2:["value":"unlocked"]].get(value,
resultMap.value = "locked" ["value":"unknown",
resultMap.descriptionText = "${linkText} is locked" "descriptionText":"Unknown lock state"]))
} else if (value == 2) { log.info "parseReportAttributeMessage() --- lock: ${resultMap.value}"
resultMap.value = "unlocked"
resultMap.descriptionText = "${linkText} is unlocked"
} else {
resultMap.value = "unknown"
resultMap.descriptionText = "${linkText} is in unknown lock state"
}
} }
else { else {
log.debug "parseReportAttributeMessage() --- ignoring attribute" log.debug "parseReportAttributeMessage() --- ignoring attribute"

View File

@@ -51,15 +51,22 @@ metadata {
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) { def resultMap = zigbee.getKnownDescription(description)
if (event.name == "power") { if (resultMap) {
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else if (resultMap.type == "power") {
def powerValue def powerValue
powerValue = (event.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power
sendEvent(name: "power", value: powerValue) powerValue = (resultMap.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(name: "power", value: powerValue)
}
} }
else { else {
sendEvent(event) sendEvent(name: resultMap.type, value: resultMap.value)
} }
} }
else { else {

View File

@@ -53,9 +53,16 @@ metadata {
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) { def resultMap = zigbee.getKnownDescription(description)
sendEvent(event) if (resultMap) {
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else {
sendEvent(name: resultMap.type, value: resultMap.value)
}
} }
else { else {
log.warn "DID NOT PARSE MESSAGE for description : $description" log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -73,9 +73,16 @@ metadata {
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) { def finalResult = zigbee.getKnownDescription(description)
sendEvent(event) if (finalResult) {
log.info finalResult
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
}
else {
sendEvent(name: finalResult.type, value: finalResult.value)
}
} }
else { else {
log.warn "DID NOT PARSE MESSAGE for description : $description" log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -161,7 +161,7 @@ private sendDeveloperReq() {
headers: [ headers: [
HOST: host HOST: host
], ],
body: [devicetype: "$token-0"]], "${selectedHue}")) body: [devicetype: "$token-0", username: "$token-0"]], "${selectedHue}"))
} }
private discoverHueBulbs() { private discoverHueBulbs() {
@@ -657,53 +657,39 @@ def setColorTemperature(childDevice, huesettings) {
} }
def setColor(childDevice, huesettings) { def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'" log.debug "Executing 'setColor($huesettings)'"
def value = [:]
def hue = null def hue = null
def sat = null def sat = null
def xy = null def xy = null
if (huesettings.hex) {
if (huesettings.hex != null) { xy = getHextoXY(huesettings.hex)
value.xy = getHextoXY(huesettings.hex) } else if (huesettings.hue && huesettings.saturation) {
} else { hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
if (huesettings.hue != null) sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
if (huesettings.saturation != null)
value.sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
} }
def alert = huesettings.alert ? huesettings.alert : "none"
// Default behavior is to turn light on def transition = huesettings.transition ? huesettings.transition : 4
value.on = true
if (huesettings.level != null) { def value = [xy: xy, sat: sat, hue: hue, alert: alert, transitiontime: transition, on: true]
if (huesettings.level <= 0)
value.on = false
else if (huesettings.level == 1)
value.bri = 1
else
value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
}
value.alert = huesettings.alert ? huesettings.alert : "none"
value.transition = huesettings.transition ? huesettings.transition : 4
// Make sure to turn off light if requested if (huesettings.level != null) {
if (huesettings.switch == "off") if (huesettings.level == 1) value.bri = 1 else value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
value.on = false value.on = value.bri > 0
}
log.debug "sending command $value" log.debug "sending command $value"
put("lights/${getId(childDevice)}/state", value) put("lights/${getId(childDevice)}/state", value)
return "Color set to $value"
} }
def nextLevel(childDevice) { def nextLevel(childDevice) {
def level = device.latestValue("level") as Integer ?: 0 def level = device.latestValue("level") as Integer ?: 0
if (level < 100) { if (level < 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
} else { }
level = 25 else {
} level = 25
setLevel(childDevice,level) }
setLevel(childDevice,level)
} }
private getId(childDevice) { private getId(childDevice) {
@@ -802,14 +788,16 @@ private getHextoXY(String colorStr) {
// Make green more vivid // Make green more vivid
if (normalizedToOne[1] > 0.04045) { if (normalizedToOne[1] > 0.04045) {
green = (float) Math.pow((normalizedToOne[1] + 0.055) / (1.0 + 0.055), 2.4); green = (float) Math.pow((normalizedToOne[1] + 0.055)
/ (1.0 + 0.055), 2.4);
} else { } else {
green = (float) (normalizedToOne[1] / 12.92); green = (float) (normalizedToOne[1] / 12.92);
} }
// Make blue more vivid // Make blue more vivid
if (normalizedToOne[2] > 0.04045) { if (normalizedToOne[2] > 0.04045) {
blue = (float) Math.pow((normalizedToOne[2] + 0.055) / (1.0 + 0.055), 2.4); blue = (float) Math.pow((normalizedToOne[2] + 0.055)
/ (1.0 + 0.055), 2.4);
} else { } else {
blue = (float) (normalizedToOne[2] / 12.92); blue = (float) (normalizedToOne[2] / 12.92);
} }
@@ -818,8 +806,8 @@ private getHextoXY(String colorStr) {
float Y = (float) (red * 0.234327 + green * 0.743075 + blue * 0.022598); float Y = (float) (red * 0.234327 + green * 0.743075 + blue * 0.022598);
float Z = (float) (red * 0.0000000 + green * 0.053077 + blue * 1.035763); float Z = (float) (red * 0.0000000 + green * 0.053077 + blue * 1.035763);
float x = (X != 0 ? X / (X + Y + Z) : 0); float x = X / (X + Y + Z);
float y = (Y != 0 ? Y / (X + Y + Z) : 0); float y = Y / (X + Y + Z);
double[] xy = new double[2]; double[] xy = new double[2];
xy[0] = x; xy[0] = x;

View File

@@ -1,158 +1,158 @@
/** /**
* Copyright 2015 SmartThings * Copyright 2015 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at: * in compliance with the License. You may obtain a copy of the License at:
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
* Notify Me When * Notify Me When
* *
* Author: SmartThings * Author: SmartThings
* Date: 2013-03-20 * Date: 2013-03-20
* *
* Change Log: * Change Log:
* 1. Todd Wackford * 1. Todd Wackford
* 2014-10-03: Added capability.button device picker and button.pushed event subscription. For Doorbell. * 2014-10-03: Added capability.button device picker and button.pushed event subscription. For Doorbell.
*/ */
definition( definition(
name: "Notify Me When", name: "Notify Me When",
namespace: "smartthings", namespace: "smartthings",
author: "SmartThings", author: "SmartThings",
description: "Receive notifications when anything happens in your home.", description: "Receive notifications when anything happens in your home.",
category: "Convenience", category: "Convenience",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact.png", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact@2x.png" iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Meta/window_contact@2x.png"
) )
preferences { preferences {
section("Choose one or more, when..."){ section("Choose one or more, when..."){
input "button", "capability.button", title: "Button Pushed", required: false, multiple: true //tw input "button", "capability.button", title: "Button Pushed", required: false, multiple: true //tw
input "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true input "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
input "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true input "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
input "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true input "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
input "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true input "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
input "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true input "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
input "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true input "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
input "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true input "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
input "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true input "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
input "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true input "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
input "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true input "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
} }
section("Send this message (optional, sends standard status message if not specified)"){ section("Send this message (optional, sends standard status message if not specified)"){
input "messageText", "text", title: "Message Text", required: false input "messageText", "text", title: "Message Text", required: false
} }
section("Via a push notification and/or an SMS message"){ section("Via a push notification and/or an SMS message"){
input("recipients", "contact", title: "Send notifications to") { input("recipients", "contact", title: "Send notifications to") {
input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false
paragraph "If outside the US please make sure to enter the proper country code" paragraph "If outside the US please make sure to enter the proper country code"
input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"] input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"]
} }
} }
section("Minimum time between messages (optional, defaults to every message)") { section("Minimum time between messages (optional, defaults to every message)") {
input "frequency", "decimal", title: "Minutes", required: false input "frequency", "decimal", title: "Minutes", required: false
} }
} }
def installed() { def installed() {
log.debug "Installed with settings: ${settings}" log.debug "Installed with settings: ${settings}"
subscribeToEvents() subscribeToEvents()
} }
def updated() { def updated() {
log.debug "Updated with settings: ${settings}" log.debug "Updated with settings: ${settings}"
unsubscribe() unsubscribe()
subscribeToEvents() subscribeToEvents()
} }
def subscribeToEvents() { def subscribeToEvents() {
subscribe(button, "button.pushed", eventHandler) //tw subscribe(button, "button.pushed", eventHandler) //tw
subscribe(contact, "contact.open", eventHandler) subscribe(contact, "contact.open", eventHandler)
subscribe(contactClosed, "contact.closed", eventHandler) subscribe(contactClosed, "contact.closed", eventHandler)
subscribe(acceleration, "acceleration.active", eventHandler) subscribe(acceleration, "acceleration.active", eventHandler)
subscribe(motion, "motion.active", eventHandler) subscribe(motion, "motion.active", eventHandler)
subscribe(mySwitch, "switch.on", eventHandler) subscribe(mySwitch, "switch.on", eventHandler)
subscribe(mySwitchOff, "switch.off", eventHandler) subscribe(mySwitchOff, "switch.off", eventHandler)
subscribe(arrivalPresence, "presence.present", eventHandler) subscribe(arrivalPresence, "presence.present", eventHandler)
subscribe(departurePresence, "presence.not present", eventHandler) subscribe(departurePresence, "presence.not present", eventHandler)
subscribe(smoke, "smoke.detected", eventHandler) subscribe(smoke, "smoke.detected", eventHandler)
subscribe(smoke, "smoke.tested", eventHandler) subscribe(smoke, "smoke.tested", eventHandler)
subscribe(smoke, "carbonMonoxide.detected", eventHandler) subscribe(smoke, "carbonMonoxide.detected", eventHandler)
subscribe(water, "water.wet", eventHandler) subscribe(water, "water.wet", eventHandler)
} }
def eventHandler(evt) { def eventHandler(evt) {
log.debug "Notify got evt ${evt}" log.debug "Notify got evt ${evt}"
if (frequency) { if (frequency) {
def lastTime = state[evt.deviceId] def lastTime = state[evt.deviceId]
if (lastTime == null || now() - lastTime >= frequency * 60000) { if (lastTime == null || now() - lastTime >= frequency * 60000) {
sendMessage(evt) sendMessage(evt)
} }
} }
else { else {
sendMessage(evt) sendMessage(evt)
} }
} }
private sendMessage(evt) { private sendMessage(evt) {
String msg = messageText String msg = messageText
Map options = [:] Map options = [:]
if (!messageText) { if (!messageText) {
msg = defaultText(evt) msg = defaultText(evt)
options = [translatable: true, triggerEvent: evt] options = [translatable: true, triggerEvent: evt]
} }
log.debug "$evt.name:$evt.value, pushAndPhone:$pushAndPhone, '$msg'" log.debug "$evt.name:$evt.value, pushAndPhone:$pushAndPhone, '$msg'"
if (location.contactBookEnabled) { if (location.contactBookEnabled) {
sendNotificationToContacts(msg, recipients, options) sendNotificationToContacts(msg, recipients, options)
} else { } else {
if (!phone || pushAndPhone != 'No') { if (!phone || pushAndPhone != 'No') {
log.debug 'sending push' log.debug 'sending push'
options.method = 'push' options.method = 'push'
//sendPush(msg) //sendPush(msg)
} }
if (phone) { if (phone) {
options.phone = phone options.phone = phone
log.debug 'sending SMS' log.debug 'sending SMS'
//sendSms(phone, msg) //sendSms(phone, msg)
} }
sendNotification(msg, options) sendNotification(msg, options)
} }
if (frequency) { if (frequency) {
state[evt.deviceId] = now() state[evt.deviceId] = now()
} }
} }
private defaultText(evt) { private defaultText(evt) {
if (evt.name == 'presence') { if (evt.name == 'presence') {
if (evt.value == 'present') { if (evt.value == 'present') {
if (includeArticle) { if (includeArticle) {
'{{ triggerEvent.linkText }} has arrived at the {{ location.name }}' '{{ triggerEvent.linkText }} has arrived at the {{ location.name }}'
} }
else { else {
'{{ triggerEvent.linkText }} has arrived at {{ location.name }}' '{{ triggerEvent.linkText }} has arrived at {{ location.name }}'
} }
} else { } else {
if (includeArticle) { if (includeArticle) {
'{{ triggerEvent.linkText }} has left the {{ location.name }}' '{{ triggerEvent.linkText }} has left the {{ location.name }}'
} }
else { else {
'{{ triggerEvent.linkText }} has left {{ location.name }}' '{{ triggerEvent.linkText }} has left {{ location.name }}'
} }
} }
} else { } else {
'{{ triggerEvent.descriptionText }}' '{{ triggerEvent.descriptionText }}'
} }
} }
private getIncludeArticle() { private getIncludeArticle() {
def name = location.name.toLowerCase() def name = location.name.toLowerCase()
def segs = name.split(" ") def segs = name.split(" ")
!(["work","home"].contains(name) || (segs.size() > 1 && (["the","my","a","an"].contains(segs[0]) || segs[0].endsWith("'s")))) !(["work","home"].contains(name) || (segs.size() > 1 && (["the","my","a","an"].contains(segs[0]) || segs[0].endsWith("'s"))))
} }

View File

@@ -40,7 +40,6 @@ preferences {
page name:"pageSetup" page name:"pageSetup"
page name:"Setup" page name:"Setup"
page name:"Settings" page name:"Settings"
page name: "timeIntervalInput"
} }
@@ -186,8 +185,7 @@ def Settings() {
} }
} }
def timeIntervalInput() { page(name: "timeIntervalInput", title: "Only during a certain time", refreshAfterSelection:true) {
dynamicPage(name: "timeIntervalInput") {
section { section {
input "startTimeType", "enum", title: "Starting at", options: [["time": "A specific time"], ["sunrise": "Sunrise"], ["sunset": "Sunset"]], defaultValue: "time", submitOnChange: true input "startTimeType", "enum", title: "Starting at", options: [["time": "A specific time"], ["sunrise": "Sunrise"], ["sunset": "Sunset"]], defaultValue: "time", submitOnChange: true
if (startTimeType in ["sunrise","sunset"]) { if (startTimeType in ["sunrise","sunset"]) {
@@ -206,10 +204,9 @@ def timeIntervalInput() {
input "ending", "time", title: "End time", required: false input "ending", "time", title: "End time", required: false
} }
} }
}
} }
def installed() { def installed() {
initialize() initialize()
} }