Compare commits

..

11 Commits

Author SHA1 Message Date
Vinay Rao
ffcacb9da5 Merge pull request #664 from SmartThingsCommunity/staging
Rolling up changes to production from staging 2016-03-25 Release
2016-03-24 13:03:24 -07:00
Vinay Rao
c45129170a Merge pull request #668 from larsfinander/staging_remove_hue
Revert "DVCSMP-1615 & DEVC-372"
2016-03-24 12:56:35 -07:00
Vinay Rao
0911651f71 Merge pull request #692 from workingmonk/bug/hotfix_multi
[DVCSMP-1657] Bug/hotfix multi
2016-03-23 15:19:18 -07:00
Vinay Rao
9cc92b1987 fixing issue with list of list of maps with multi parsing
adding return type to methods
2016-03-23 15:13:33 -07:00
Lars Finander
633bef2ac5 Revert "DVCSMP-1615 & DEVC-372"
This reverts commit 6fbef3b297.
(temporary for staging, MArch 25 deploy)
2016-03-22 14:29:32 -07:00
Vinay Rao
a8357e7644 Merge pull request #652 from SmartThingsCommunity/master
Rolling up changes from master to staging
2016-03-18 13:38:31 -07:00
Vinay Rao
024a6cb698 Merge pull request #632 from SmartThingsCommunity/staging
Rolling up changes to production from staging 2016-03-17 Release
2016-03-18 10:43:22 -07:00
Vinay Rao
62a965d90b Merge pull request #599 from SmartThingsCommunity/staging
Rolling up changes to production from staging 2016-03-10 Release
2016-03-10 16:26:26 -08:00
Vinay Rao
515b268374 Merge pull request #549 from SmartThingsCommunity/staging
Rolling up changes to production from staging 2016-02-25 Release
2016-02-26 10:51:42 -08:00
Vinay Rao
a103d437c2 Merge pull request #523 from SmartThingsCommunity/staging
Deploy to production 2/18
2016-02-18 09:28:02 -08:00
Vinay Rao
bdd88deb99 Merge pull request #504 from SmartThingsCommunity/staging
Merging changes from staging to production
2016-02-11 22:40:38 -08:00
10 changed files with 223 additions and 439 deletions

View File

@@ -1,42 +0,0 @@
/**
* 내가만든장치
*
* Copyright 2016 이진희
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "내가만든장치", namespace: "내가만든장치", author: "이진희") {
capability "Health Check"
capability "Test Capability"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
// TODO: define your main and details tiles here
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'checkInterval' attribute
}
// handle commands
def ping() {
log.debug "Executing 'ping'"
// TODO: handle 'ping' command
}

View File

@@ -17,13 +17,16 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"rich-control"){ multiAttributeTile(name:"rich-control"){
tileAttribute ("", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#F3C200" attributeState "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#F3C200"
} }
tileAttribute ("serialNumber", key: "SECONDARY_CONTROL") { tileAttribute ("serialNumber", key: "SECONDARY_CONTROL") {
attributeState "default", label:'SN: ${currentValue}' attributeState "default", label:'SN: ${currentValue}'
} }
} }
standardTile("icon", "icon", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
state "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#FFFFFF"
}
valueTile("serialNumber", "device.serialNumber", decoration: "flat", height: 1, width: 2, inactiveLabel: false) { valueTile("serialNumber", "device.serialNumber", decoration: "flat", height: 1, width: 2, inactiveLabel: false) {
state "default", label:'SN: ${currentValue}' state "default", label:'SN: ${currentValue}'
} }
@@ -31,7 +34,7 @@ metadata {
state "default", label:'${currentValue}', height: 1, width: 2, inactiveLabel: false state "default", label:'${currentValue}', height: 1, width: 2, inactiveLabel: false
} }
main (["rich-control"]) main (["icon"])
details(["rich-control", "networkAddress"]) details(["rich-control", "networkAddress"])
} }
} }
@@ -72,7 +75,6 @@ def parse(description) {
} }
else if (contentType?.contains("xml")) { else if (contentType?.contains("xml")) {
log.debug "HUE BRIDGE ALREADY PRESENT" log.debug "HUE BRIDGE ALREADY PRESENT"
parent.hubVerification(device.hub.id, msg.body)
} }
} }
} }

View File

@@ -3,7 +3,6 @@
* *
* Author: SmartThings * Author: SmartThings
*/ */
// for the UI // for the UI
metadata { metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
@@ -28,10 +27,10 @@ metadata {
tiles (scale: 2){ tiles (scale: 2){
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){ multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn" attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)" attributeState "level", action:"switch level.setLevel", range:"(0..100)"
@@ -44,10 +43,16 @@ metadata {
} }
} }
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") { controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature" state "colorTemperature", action:"color temperature.setColorTemperature"
} }
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K' state "colorTemperature", label: '${currentValue} K'
} }
@@ -55,12 +60,29 @@ metadata {
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single" state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single"
} }
standardTile("refresh", "device.switch", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel"
}
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
state "level", label: 'Level ${currentValue}%'
}
controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) {
state "saturation", action:"color control.setSaturation"
}
valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") {
state "saturation", label: 'Sat ${currentValue} '
}
controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) {
state "hue", action:"color control.setHue"
}
valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") {
state "hue", label: 'Hue ${currentValue} '
}
main(["rich-control"]) main(["switch"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"]) details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"])
} }
} }
@@ -105,34 +127,34 @@ void nextLevel() {
void setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
parent.setLevel(this, percent) parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%") sendEvent(name: "level", value: percent)
} }
void setSaturation(percent) { void setSaturation(percent) {
log.debug "Executing 'setSaturation'" log.debug "Executing 'setSaturation'"
parent.setSaturation(this, percent) parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false) sendEvent(name: "saturation", value: percent)
} }
void setHue(percent) { void setHue(percent) {
log.debug "Executing 'setHue'" log.debug "Executing 'setHue'"
parent.setHue(this, percent) parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false) sendEvent(name: "hue", value: percent)
} }
void setColor(value) { void setColor(value) {
log.debug "setColor: ${value}, $this" log.debug "setColor: ${value}, $this"
parent.setColor(this, value) parent.setColor(this, value)
if (value.hue) { sendEvent(name: "hue", value: value.hue, displayed: false)} if (value.hue) { sendEvent(name: "hue", value: value.hue)}
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation, displayed: false)} if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)}
if (value.hex) { sendEvent(name: "color", value: value.hex)} if (value.hex) { sendEvent(name: "color", value: value.hex)}
if (value.level) { sendEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")} if (value.level) { sendEvent(name: "level", value: value.level)}
sendEvent(name: "switch", value: "on") if (value.switch) { sendEvent(name: "switch", value: value.switch)}
} }
void reset() { void reset() {
log.debug "Executing 'reset'" log.debug "Executing 'reset'"
def value = [level:100, saturation:56, hue:23] def value = [level:100, hex:"#90C638", saturation:56, hue:23]
setAdjustedColor(value) setAdjustedColor(value)
parent.poll() parent.poll()
} }

View File

@@ -36,6 +36,13 @@ metadata {
} }
} }
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel" state "level", action:"switch level.setLevel"
} }
@@ -44,7 +51,7 @@ metadata {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
main(["rich-control"]) main(["switch"])
details(["rich-control", "refresh"]) details(["rich-control", "refresh"])
} }
} }

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 << getContactResult(part, description) results.addAll(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 getAccelerationResult(part, description) { private Map 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 getAccelerationResult(part, description) {
] ]
} }
private getTempResult(part, description) { private Map 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 getTempResult(part, description) {
] ]
} }
private getXyzResult(results, description) { private Map 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 getXyzResult(results, description) {
] ]
} }
private getBatteryResult(part, description) { private Map 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 getBatteryResult(part, description) {
] ]
} }
private getRssiResult(part, description, lastHop=false) { private Map 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 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 getLqiResult(part, description, lastHop=false) { private Map 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

@@ -57,7 +57,7 @@ def parse(String description) {
return result return result
} }
def sensorValueEvent(value) { def sensorValueEvent(Short value) {
if (value) { if (value) {
createEvent(name: "motion", value: "active", descriptionText: "$device.displayName detected motion") createEvent(name: "motion", value: "active", descriptionText: "$device.displayName detected motion")
} else { } else {
@@ -94,24 +94,24 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
{ {
def result = [] def result = []
if (cmd.notificationType == 0x07) { if (cmd.notificationType == 0x07) {
if (cmd.v1AlarmType == 0x07) { // special case for nonstandard messages from Monoprice ensors if (cmd.event == 0x01 || cmd.event == 0x02) {
result << sensorValueEvent(cmd.v1AlarmLevel)
} else if (cmd.event == 0x01 || cmd.event == 0x02 || cmd.event == 0x07 || cmd.event == 0x08) {
result << sensorValueEvent(1) result << sensorValueEvent(1)
} else if (cmd.event == 0x00) {
result << sensorValueEvent(0)
} else if (cmd.event == 0x03) { } else if (cmd.event == 0x03) {
result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName covering was removed", isStateChange: true) result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
result << response(zwave.batteryV1.batteryGet()) result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId))
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
} else if (cmd.event == 0x05 || cmd.event == 0x06) { } else if (cmd.event == 0x05 || cmd.event == 0x06) {
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true) result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
} else if (cmd.event == 0x07) {
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
result << sensorValueEvent(1)
} }
} else if (cmd.notificationType) { } else if (cmd.notificationType) {
def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}" def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}"
result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, isStateChange: true, displayed: false) result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false)
} else { } else {
def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive" def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"
result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, isStateChange: true, displayed: false) result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false)
} }
result result
} }

View File

@@ -61,44 +61,37 @@ def parse(String description) {
zwaveEvent(cmd, results) zwaveEvent(cmd, results)
} }
} }
log.debug "'$description' parsed to ${results.inspect()}" // log.debug "\"$description\" parsed to ${results.inspect()}"
return results return results
} }
def createSmokeOrCOEvents(name, results) { def createSmokeOrCOEvents(name, results) {
def text = null def text = null
switch (name) { if (name == "smoke") {
case "smoke": text = "$device.displayName smoke was detected!"
text = "$device.displayName smoke was detected!" // these are displayed:false because the composite event is the one we want to see in the app
// these are displayed:false because the composite event is the one we want to see in the app results << createEvent(name: "smoke", value: "detected", descriptionText: text, displayed: false)
results << createEvent(name: "smoke", value: "detected", descriptionText: text, displayed: false) } else if (name == "carbonMonoxide") {
break text = "$device.displayName carbon monoxide was detected!"
case "carbonMonoxide": results << createEvent(name: "carbonMonoxide", value: "detected", descriptionText: text, displayed: false)
text = "$device.displayName carbon monoxide was detected!" } else if (name == "tested") {
results << createEvent(name: "carbonMonoxide", value: "detected", descriptionText: text, displayed: false) text = "$device.displayName was tested"
break results << createEvent(name: "smoke", value: "tested", descriptionText: text, displayed: false)
case "tested": results << createEvent(name: "carbonMonoxide", value: "tested", descriptionText: text, displayed: false)
text = "$device.displayName was tested" } else if (name == "smokeClear") {
results << createEvent(name: "smoke", value: "tested", descriptionText: text, displayed: false) text = "$device.displayName smoke is clear"
results << createEvent(name: "carbonMonoxide", value: "tested", descriptionText: text, displayed: false) results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
break name = "clear"
case "smokeClear": } else if (name == "carbonMonoxideClear") {
text = "$device.displayName smoke is clear" text = "$device.displayName carbon monoxide is clear"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false) results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
name = "clear" name = "clear"
break } else if (name == "testClear") {
case "carbonMonoxideClear": text = "$device.displayName smoke is clear"
text = "$device.displayName carbon monoxide is clear" results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false) results << createEvent(name: "carbonMonoxide", value: "clear", displayed: false)
name = "clear" name = "clear"
break
case "testClear":
text = "$device.displayName test cleared"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
results << createEvent(name: "carbonMonoxide", value: "clear", displayed: false)
name = "clear"
break
} }
// This composite event is used for updating the tile // This composite event is used for updating the tile
results << createEvent(name: "alarmState", value: name, descriptionText: text) results << createEvent(name: "alarmState", value: name, descriptionText: text)
@@ -124,10 +117,8 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
createSmokeOrCOEvents(cmd.alarmLevel ? "tested" : "testClear", results) createSmokeOrCOEvents(cmd.alarmLevel ? "tested" : "testClear", results)
break break
case 13: // sent every hour -- not sure what this means, just a wake up notification? case 13: // sent every hour -- not sure what this means, just a wake up notification?
if (cmd.alarmLevel == 255) { if (cmd.alarmLevel != 255) {
results << createEvent(descriptionText: "$device.displayName checked in", isStateChange: false) results << createEvent(descriptionText: "$device.displayName code 13 is $cmd.alarmLevel", displayed: true)
} else {
results << createEvent(descriptionText: "$device.displayName code 13 is $cmd.alarmLevel", isStateChange:true, displayed:false)
} }
// Clear smoke in case they pulled batteries and we missed the clear msg // Clear smoke in case they pulled batteries and we missed the clear msg
@@ -136,8 +127,9 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
} }
// Check battery if we don't have a recent battery event // Check battery if we don't have a recent battery event
if (!state.lastbatt || (now() - state.lastbatt) >= 48*60*60*1000) { def prevBattery = device.currentState("battery")
results << response(zwave.batteryV1.batteryGet()) if (!prevBattery || (new Date().time - prevBattery.date.time)/60000 >= 60 * 53) {
results << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format())
} }
break break
default: default:
@@ -166,17 +158,12 @@ def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd,
} }
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) { def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) {
results << new physicalgraph.device.HubAction(zwave.wakeUpV1.wakeUpNoMoreInformation().format())
results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false) results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
if (!state.lastbatt || (now() - state.lastbatt) >= 56*60*60*1000) {
results << response(zwave.batteryV1.batteryGet(), "delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation())
} else {
results << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
}
} }
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) { def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
def map = [ name: "battery", unit: "%", isStateChange: true ] def map = [ name: "battery", unit: "%" ]
state.lastbatt = now()
if (cmd.batteryLevel == 0xFF) { if (cmd.batteryLevel == 0xFF) {
map.value = 1 map.value = 1
map.descriptionText = "$device.displayName battery is low!" map.descriptionText = "$device.displayName battery is low!"

View File

@@ -24,7 +24,7 @@ definition(
category: "SmartThings Labs", category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/hue.png", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/hue.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/hue@2x.png", iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/hue@2x.png",
singleInstance: true //singleInstance: true
) )
preferences { preferences {
@@ -68,7 +68,7 @@ def bridgeDiscovery(params=[:])
} }
//setup.xml request every 3 seconds except on discoveries //setup.xml request every 3 seconds except on discoveries
if(((bridgeRefreshCount % 3) == 0) && ((bridgeRefreshCount % 5) != 0)) { if(((bridgeRefreshCount % 1) == 0) && ((bridgeRefreshCount % 5) != 0)) {
verifyHueBridges() verifyHueBridges()
} }
@@ -175,7 +175,6 @@ private discoverHueBulbs() {
} }
private verifyHueBridge(String deviceNetworkId, String host) { private verifyHueBridge(String deviceNetworkId, String host) {
log.trace "Verify Hue Bridge $deviceNetworkId"
sendHubCommand(new physicalgraph.device.HubAction([ sendHubCommand(new physicalgraph.device.HubAction([
method: "GET", method: "GET",
path: "/description.xml", path: "/description.xml",
@@ -603,20 +602,6 @@ def parse(childDevice, description) {
} }
} }
def hubVerification(bodytext) {
log.trace "Bridge sent back description.xml for verification"
def body = new XmlSlurper().parseText(bodytext)
if (body?.device?.modelName?.text().startsWith("Philips hue bridge")) {
def bridges = getHueBridges()
def bridge = bridges.find {it?.key?.contains(body?.device?.UDN?.text())}
if (bridge) {
bridge.value << [name:body?.device?.friendlyName?.text(), serialNumber:body?.device?.serialNumber?.text(), verified: true]
} else {
log.error "/description.xml returned a bridge that didn't exist"
}
}
}
def on(childDevice) { def on(childDevice) {
log.debug "Executing 'on'" log.debug "Executing 'on'"
put("lights/${getId(childDevice)}/state", [on: true]) put("lights/${getId(childDevice)}/state", [on: true])
@@ -658,25 +643,21 @@ def setColorTemperature(childDevice, huesettings) {
def setColor(childDevice, huesettings) { def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'" log.debug "Executing 'setColor($huesettings)'"
def hue = null def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
def sat = null def sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
def xy = null
if (huesettings.hex) {
xy = getHextoXY(huesettings.hex)
} else if (huesettings.hue && huesettings.saturation) {
hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
}
def alert = huesettings.alert ? huesettings.alert : "none" def alert = huesettings.alert ? huesettings.alert : "none"
def transition = huesettings.transition ? huesettings.transition : 4 def transition = huesettings.transition ? huesettings.transition : 4
def value = [xy: xy, sat: sat, hue: hue, alert: alert, transitiontime: transition, on: true] def value = [sat: sat, hue: hue, alert: alert, transitiontime: transition]
if (huesettings.level != null) { if (huesettings.level != null) {
if (huesettings.level == 1) value.bri = 1 else value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255) if (huesettings.level == 1) value.bri = 1 else value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
value.on = value.bri > 0 value.on = value.bri > 0
} }
if (huesettings.switch) {
value.on = huesettings.switch == "on"
}
log.debug "sending command $value" log.debug "sending command $value"
put("lights/${getId(childDevice)}/state", value) put("lights/${getId(childDevice)}/state", value)
} }
@@ -762,59 +743,6 @@ private getBridgeIP() {
return host return host
} }
private getHextoXY(String colorStr) {
// For the hue bulb the corners of the triangle are:
// -Red: 0.675, 0.322
// -Green: 0.4091, 0.518
// -Blue: 0.167, 0.04
def cred = Integer.valueOf( colorStr.substring( 1, 3 ), 16 )
def cgreen = Integer.valueOf( colorStr.substring( 3, 5 ), 16 )
def cblue = Integer.valueOf( colorStr.substring( 5, 7 ), 16 )
double[] normalizedToOne = new double[3];
normalizedToOne[0] = (cred / 255);
normalizedToOne[1] = (cgreen / 255);
normalizedToOne[2] = (cblue / 255);
float red, green, blue;
// Make red more vivid
if (normalizedToOne[0] > 0.04045) {
red = (float) Math.pow(
(normalizedToOne[0] + 0.055) / (1.0 + 0.055), 2.4);
} else {
red = (float) (normalizedToOne[0] / 12.92);
}
// Make green more vivid
if (normalizedToOne[1] > 0.04045) {
green = (float) Math.pow((normalizedToOne[1] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
green = (float) (normalizedToOne[1] / 12.92);
}
// Make blue more vivid
if (normalizedToOne[2] > 0.04045) {
blue = (float) Math.pow((normalizedToOne[2] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
blue = (float) (normalizedToOne[2] / 12.92);
}
float X = (float) (red * 0.649926 + green * 0.103455 + blue * 0.197109);
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 x = X / (X + Y + Z);
float y = Y / (X + Y + Z);
double[] xy = new double[2];
xy[0] = x;
xy[1] = y;
return xy;
}
private Integer convertHexToInt(hex) { private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16) Integer.parseInt(hex,16)
} }

View File

@@ -1,50 +0,0 @@
/**
* test 앱
*
* Copyright 2016 이진희
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
definition(
name: "test 앱",
namespace: "test앱",
author: "이진희",
description: "\uD14C\uC2A4\uD2B8\uC785\uB2C8\uB2E4",
category: "",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
preferences {
section("Title") {
// TODO: put inputs here
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
// TODO: subscribe to attributes, devices, locations, etc.
}
// TODO: implement event handlers

View File

@@ -1,17 +1,11 @@
/** /**
* Vacation Lighting Director * Vacation Lighting Director
* *
* Version 2.5 - Moved scheduling over to Cron and added time as a trigger. * Version 2.4 - Added information paragraphs
* Cleaned up formatting and some typos.
* Updated license.
* Made people option optional
* Added sttement to unschedule on mode change if people option is not selected
*
* Version 2.4 - Added information paragraphs
* *
* Source code can be found here: https://github.com/tslagle13/SmartThings/blob/master/smartapps/tslagle13/vacation-lighting-director.groovy * Source code can be found here: https://github.com/tslagle13/SmartThings/blob/master/smartapps/tslagle13/vacation-lighting-director.groovy
* *
* Copyright 2016 Tim Slagle * Copyright 2015 Tim Slagle
* *
* 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:
@@ -57,7 +51,8 @@ def pageSetup() {
return dynamicPage(pageProperties) { return dynamicPage(pageProperties) {
section(""){ section(""){
paragraph "This app can be used to make your home seem occupied anytime you are away from your home. " + paragraph "This app can be used to make your home seem occupied anytime you are away from your home. " +
"Please use each of the the sections below to setup the different preferences to your liking. " "Please use each othe the sections below to setup the different preferences to your liking. " +
"I recommend this app be used with at least two away modes. An example would be 'Away Day' 'and Away Night'. "
} }
section("Setup Menu") { section("Setup Menu") {
href "Setup", title: "Setup", description: "", state:greyedOut() href "Setup", title: "Setup", description: "", state:greyedOut()
@@ -75,7 +70,7 @@ def Setup() {
def newMode = [ def newMode = [
name: "newMode", name: "newMode",
type: "mode", type: "mode",
title: "Modes", title: "Which?",
multiple: true, multiple: true,
required: true required: true
] ]
@@ -101,6 +96,14 @@ def Setup() {
required: true, required: true,
] ]
def people = [
name: "people",
type: "capability.presenceSensor",
title: "If these people are home do not change light status",
required: true,
multiple: true
]
def pageName = "Setup" def pageName = "Setup"
def pageProperties = [ def pageProperties = [
@@ -113,11 +116,10 @@ def Setup() {
section(""){ section(""){
paragraph "In this section you need to setup the deatils of how you want your lighting to be affected while " + paragraph "In this section you need to setup the deatils of how you want your lighting to be affected while " +
"you are away. All of these settings are required in order for the simulator to run correctly." paragraph "you are away. All of these settings are required in order for the simulator to run correctly."
} }
section("Simulator Triggers") { section("Which mode change triggers the simulator? (This app will only run in selected mode(s))") {
input newMode input newMode
href "timeIntervalInput", title: "Times", description: timeIntervalLabel(), refreshAfterSelection:true
} }
section("Light switches to turn on/off") { section("Light switches to turn on/off") {
input switches input switches
@@ -128,6 +130,9 @@ def Setup() {
section("Number of active lights at any given time") { section("Number of active lights at any given time") {
input number_of_active_lights input number_of_active_lights
} }
section("People") {
input people
}
} }
} }
@@ -157,29 +162,18 @@ def Settings() {
title: "Settings", title: "Settings",
nextPage: "pageSetup" nextPage: "pageSetup"
] ]
def people = [
name: "people",
type: "capability.presenceSensor",
title: "If these people are home do not change light status",
required: false,
multiple: true
]
return dynamicPage(pageProperties) { return dynamicPage(pageProperties) {
section(""){ section(""){
paragraph "In this section you can restrict how your simulator runs. For instance you can restrict on which days it will run " + paragraph "In this section you can restrict how your simulator runs. For instance you can restrict on which days it will run " +
"as well as a delay for the simulator to start after it is in the correct mode. Delaying the simulator helps with false starts based on a incorrect mode change." paragraph "as well as a delay for the simulator to start after it is in the correct mode. Delaying the simulator helps with false starts based on a incorrect mode change."
} }
section("Delay to start simulator") { section("Delay to start simulator") {
input falseAlarmThreshold input falseAlarmThreshold
} }
section("People") {
paragraph "Not using this setting may cause some lights to remain on when you arrive home"
input people
}
section("More options") { section("More options") {
href "timeIntervalInput", title: "Only during a certain time", description: getTimeLabel(starting, ending), state: greyedOutTime(starting, ending), refreshAfterSelection:true
input days input days
} }
} }
@@ -187,24 +181,9 @@ def Settings() {
page(name: "timeIntervalInput", title: "Only during a certain time", refreshAfterSelection:true) { page(name: "timeIntervalInput", title: "Only during a certain time", refreshAfterSelection:true) {
section { section {
input "startTimeType", "enum", title: "Starting at", options: [["time": "A specific time"], ["sunrise": "Sunrise"], ["sunset": "Sunset"]], defaultValue: "time", submitOnChange: true input "starting", "time", title: "Starting", required: false
if (startTimeType in ["sunrise","sunset"]) { input "ending", "time", title: "Ending", required: false
input "startTimeOffset", "number", title: "Offset in minutes (+/-)", range: "*..*", required: false
}
else {
input "starting", "time", title: "Start time", required: false
}
} }
section {
input "endTimeType", "enum", title: "Ending at", options: [["time": "A specific time"], ["sunrise": "Sunrise"], ["sunset": "Sunset"]], defaultValue: "time", submitOnChange: true
if (endTimeType in ["sunrise","sunset"]) {
input "endTimeOffset", "number", title: "Offset in minutes (+/-)", range: "*..*", required: false
}
else {
input "ending", "time", title: "End time", required: false
}
}
} }
def installed() { def installed() {
@@ -222,13 +201,10 @@ def initialize(){
if (newMode != null) { if (newMode != null) {
subscribe(location, modeChangeHandler) subscribe(location, modeChangeHandler)
} }
if (starting != null) {
schedule(starting, modeChangeHandler)
}
log.debug "Installed with settings: ${settings}"
} }
def modeChangeHandler(evt) { def modeChangeHandler(evt) {
log.debug "Mode change to: ${evt.value}"
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 2 * 60 def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 2 * 60
runIn(delay, scheduleCheck) runIn(delay, scheduleCheck)
} }
@@ -236,54 +212,48 @@ def modeChangeHandler(evt) {
//Main logic to pick a random set of lights from the large set of lights to turn on and then turn the rest off //Main logic to pick a random set of lights from the large set of lights to turn on and then turn the rest off
def scheduleCheck(evt) { def scheduleCheck(evt) {
if(allOk){ if(allOk){
log.debug("Running") log.debug("Running")
// turn off all the switches // turn off all the switches
switches.off() switches.off()
// grab a random switch // grab a random switch
def random = new Random() def random = new Random()
def inactive_switches = switches def inactive_switches = switches
for (int i = 0 ; i < number_of_active_lights ; i++) { for (int i = 0 ; i < number_of_active_lights ; i++) {
// if there are no inactive switches to turn on then let's break // if there are no inactive switches to turn on then let's break
if (inactive_switches.size() == 0){ if (inactive_switches.size() == 0){
break break
}
// grab a random switch and turn it on
def random_int = random.nextInt(inactive_switches.size())
inactive_switches[random_int].on()
// then remove that switch from the pool off switches that can be turned on
inactive_switches.remove(random_int)
}
// re-run again when the frequency demands it
schedule("0 0/${frequency_minutes} * 1/1 * ? *", scheduleCheck)
} }
//Check to see if mode is ok but not time/day. If mode is still ok, check again after frequency period.
else if (modeOk) { // grab a random switch and turn it on
log.debug("mode OK. Running again") def random_int = random.nextInt(inactive_switches.size())
switches.off() inactive_switches[random_int].on()
}
//if none is ok turn off frequency check and turn off lights. // then remove that switch from the pool off switches that can be turned on
else { inactive_switches.remove(random_int)
if(people){ }
//don't turn off lights if anyone is home
if(someoneIsHome()){ // re-run again when the frequency demands it
log.debug("Stopping Check for Light") runIn(frequency_minutes * 60, scheduleCheck)
unschedule() }
} //Check to see if mode is ok but not time/day. If mode is still ok, check again after frequency period.
else{ else if (modeOk) {
log.debug("Stopping Check for Light and turning off all lights") log.debug("mode OK. Running again")
switches.off() runIn(frequency_minutes * 60, scheduleCheck)
unschedule() switches.off()
} }
//if none is ok turn off frequency check and turn off lights.
else if(people){
//don't turn off lights if anyone is home
if(someoneIsHome()){
log.debug("Stopping Check for Light")
} }
else if (!modeOk) { else{
unschedule() log.debug("Stopping Check for Light and turning off all lights")
} switches.off()
} }
}
} }
@@ -316,6 +286,26 @@ private getDaysOk() {
result result
} }
private getTimeOk() {
def result = true
if (starting && ending) {
def currTime = now()
def start = timeToday(starting).time
def stop = timeToday(ending).time
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
}
else if (starting){
result = currTime >= start
}
else if (ending){
result = currTime <= stop
}
log.trace "timeOk = $result"
result
}
private getHomeIsEmpty() { private getHomeIsEmpty() {
def result = true def result = true
@@ -340,59 +330,25 @@ private getSomeoneIsHome() {
return result return result
} }
private getTimeOk() {
def result = true //gets the label for time restriction. Label phrasing changes depending on if there is both start and stop times or just one start/stop time.
def start = timeWindowStart() def getTimeLabel(starting, ending){
def stop = timeWindowStop()
if (start && stop && location.timeZone) { def timeLabel = "Tap to set"
result = timeOfDayIsBetween(start, stop, new Date(), location.timeZone)
} if(starting && ending){
log.trace "timeOk = $result" timeLabel = "Between" + " " + hhmm(starting) + " " + "and" + " " + hhmm(ending)
result }
} else if (starting) {
timeLabel = "Start at" + " " + hhmm(starting)
private timeWindowStart() { }
def result = null else if(ending){
if (startTimeType == "sunrise") { timeLabel = "End at" + hhmm(ending)
result = location.currentState("sunriseTime")?.dateValue }
if (result && startTimeOffset) { timeLabel
result = new Date(result.time + Math.round(startTimeOffset * 60000))
}
}
else if (startTimeType == "sunset") {
result = location.currentState("sunsetTime")?.dateValue
if (result && startTimeOffset) {
result = new Date(result.time + Math.round(startTimeOffset * 60000))
}
}
else if (starting && location.timeZone) {
result = timeToday(starting, location.timeZone)
}
log.trace "timeWindowStart = ${result}"
result
}
private timeWindowStop() {
def result = null
if (endTimeType == "sunrise") {
result = location.currentState("sunriseTime")?.dateValue
if (result && endTimeOffset) {
result = new Date(result.time + Math.round(endTimeOffset * 60000))
}
}
else if (endTimeType == "sunset") {
result = location.currentState("sunsetTime")?.dateValue
if (result && endTimeOffset) {
result = new Date(result.time + Math.round(endTimeOffset * 60000))
}
}
else if (ending && location.timeZone) {
result = timeToday(ending, location.timeZone)
}
log.trace "timeWindowStop = ${result}"
result
} }
//fomrats time to readable format for time label
private hhmm(time, fmt = "h:mm a") private hhmm(time, fmt = "h:mm a")
{ {
def t = timeToday(time, location.timeZone) def t = timeToday(time, location.timeZone)
@@ -401,41 +357,6 @@ private hhmm(time, fmt = "h:mm a")
f.format(t) f.format(t)
} }
private timeIntervalLabel() {
def start = ""
switch (startTimeType) {
case "time":
if (ending) {
start += hhmm(starting)
}
break
case "sunrise":
case "sunset":
start += startTimeType[0].toUpperCase() + startTimeType[1..-1]
if (startTimeOffset) {
start += startTimeOffset > 0 ? "+${startTimeOffset} min" : "${startTimeOffset} min"
}
break
}
def finish = ""
switch (endTimeType) {
case "time":
if (ending) {
finish += hhmm(ending)
}
break
case "sunrise":
case "sunset":
finish += endTimeType[0].toUpperCase() + endTimeType[1..-1]
if (endTimeOffset) {
finish += endTimeOffset > 0 ? "+${endTimeOffset} min" : "${endTimeOffset} min"
}
break
}
start && finish ? "${start} to ${finish}" : ""
}
//sets complete/not complete for the setup section on the main dynamic page //sets complete/not complete for the setup section on the main dynamic page
def greyedOut(){ def greyedOut(){
def result = "" def result = ""
@@ -448,7 +369,16 @@ def greyedOut(){
//sets complete/not complete for the settings section on the main dynamic page //sets complete/not complete for the settings section on the main dynamic page
def greyedOutSettings(){ def greyedOutSettings(){
def result = "" def result = ""
if (people || days || falseAlarmThreshold ) { if (starting || ending || days || falseAlarmThreshold) {
result = "complete"
}
result
}
//sets complete/not complete for time restriction section in settings
def greyedOutTime(starting, ending){
def result = ""
if (starting || ending) {
result = "complete" result = "complete"
} }
result result