mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 13:21:53 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a3614547e | ||
|
|
90dee51255 | ||
|
|
7b7fdd43cd | ||
|
|
9059718818 | ||
|
|
9d7c66c7af | ||
|
|
8e81967227 | ||
|
|
8c6c68f102 | ||
|
|
661f8b3bc0 | ||
|
|
a62d825f69 | ||
|
|
1890147221 | ||
|
|
2fb5f8c78c | ||
|
|
b44356248c | ||
|
|
c30af84d70 | ||
|
|
5cf72c644c | ||
|
|
8ae9b06022 | ||
|
|
5e93bca030 | ||
|
|
71fc8e7f5f | ||
|
|
30dedde0df |
@@ -15,6 +15,7 @@
|
||||
* Author: SmartThings
|
||||
* Date: 2013-12-04
|
||||
*/
|
||||
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
|
||||
metadata {
|
||||
definition (name: "CentraLite Dimmer", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Switch Level"
|
||||
@@ -25,7 +26,6 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B04,0B05", outClusters: "0019"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Logitech Harmony Activity", namespace: "smartthings", author: "Juan Risso") {
|
||||
definition (name: "Harmony Activity", namespace: "smartthings", author: "Juan Risso") {
|
||||
capability "Switch"
|
||||
capability "Actuator"
|
||||
capability "Refresh"
|
||||
|
||||
@@ -17,44 +17,50 @@ metadata {
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
|
||||
}
|
||||
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "unreachable", label: "?", action:"refresh.refresh", icon:"st.switches.light.off", backgroundColor:"#666666"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'Turning on', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOff", label:'Turning off', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
|
||||
tileAttribute ("device.color", key: "COLOR_CONTROL") {
|
||||
attributeState "color", action:"setColor"
|
||||
}
|
||||
|
||||
tileAttribute ("device.model", key: "SECONDARY_CONTROL") {
|
||||
attributeState "model", label: '${currentValue}'
|
||||
}
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
valueTile("null", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:''
|
||||
}
|
||||
|
||||
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
|
||||
state "color", action:"setColor"
|
||||
}
|
||||
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false, range:"(0..100)") {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
valueTile("level", "device.level", inactiveLabel: false, icon: "st.illuminance.illuminance.light", decoration: "flat") {
|
||||
state "level", label: '${currentValue}%'
|
||||
}
|
||||
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..9000)") {
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 2, width: 4, inactiveLabel: false, range:"(2700..9000)") {
|
||||
state "colorTemp", action:"color temperature.setColorTemperature"
|
||||
}
|
||||
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", height: 2, width: 2) {
|
||||
state "colorTemp", label: '${currentValue}K'
|
||||
}
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "refresh", "level", "levelSliderControl", "rgbSelector", "colorTempSliderControl", "colorTemp"])
|
||||
main "switch"
|
||||
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +76,7 @@ def parse(String description) {
|
||||
def setHue(percentage) {
|
||||
log.debug "setHue ${percentage}"
|
||||
parent.logErrors(logObject: log) {
|
||||
def resp = parent.apiPUT("/lights/${device.deviceNetworkId}/color", [color: "hue:${percentage * 3.6}"])
|
||||
def resp = parent.apiPUT("/lights/${selector()}/state", [color: "hue:${percentage * 3.6}", power: "on"])
|
||||
if (resp.status < 300) {
|
||||
sendEvent(name: "hue", value: percentage)
|
||||
sendEvent(name: "switch", value: "on")
|
||||
@@ -83,7 +89,7 @@ def setHue(percentage) {
|
||||
def setSaturation(percentage) {
|
||||
log.debug "setSaturation ${percentage}"
|
||||
parent.logErrors(logObject: log) {
|
||||
def resp = parent.apiPUT("/lights/${device.deviceNetworkId}/color", [color: "saturation:${percentage / 100}"])
|
||||
def resp = parent.apiPUT("/lights/${selector()}/state", [color: "saturation:${percentage / 100}", power: "on"])
|
||||
if (resp.status < 300) {
|
||||
sendEvent(name: "saturation", value: percentage)
|
||||
sendEvent(name: "switch", value: "on")
|
||||
@@ -114,7 +120,7 @@ def setColor(Map color) {
|
||||
}
|
||||
}
|
||||
parent.logErrors(logObject:log) {
|
||||
def resp = parent.apiPUT("/lights/${device.deviceNetworkId}/color", [color: attrs.join(" ")])
|
||||
def resp = parent.apiPUT("/lights/${selector()}/state", [color: attrs.join(" "), power: "on"])
|
||||
if (resp.status < 300) {
|
||||
sendEvent(name: "color", value: color.hex)
|
||||
sendEvent(name: "switch", value: "on")
|
||||
@@ -135,9 +141,10 @@ def setLevel(percentage) {
|
||||
return off() // if the brightness is set to 0, just turn it off
|
||||
}
|
||||
parent.logErrors(logObject:log) {
|
||||
def resp = parent.apiPUT("/lights/${device.deviceNetworkId}/color", ["color": "brightness:${percentage / 100}"])
|
||||
def resp = parent.apiPUT("/lights/${selector()}/state", ["brightness": percentage / 100, "power": "on"])
|
||||
if (resp.status < 300) {
|
||||
sendEvent(name: "level", value: percentage)
|
||||
sendEvent(name: "switch.setLevel", value: percentage)
|
||||
sendEvent(name: "switch", value: "on")
|
||||
} else {
|
||||
log.error("Bad setLevel result: [${resp.status}] ${resp.data}")
|
||||
@@ -148,7 +155,7 @@ def setLevel(percentage) {
|
||||
def setColorTemperature(kelvin) {
|
||||
log.debug "Executing 'setColorTemperature' to ${kelvin}"
|
||||
parent.logErrors() {
|
||||
def resp = parent.apiPUT("/lights/${device.deviceNetworkId}/color", [color: "kelvin:${kelvin}"])
|
||||
def resp = parent.apiPUT("/lights/${selector()}/state", [color: "kelvin:${kelvin}", power: "on"])
|
||||
if (resp.status < 300) {
|
||||
sendEvent(name: "colorTemperature", value: kelvin)
|
||||
sendEvent(name: "color", value: "#ffffff")
|
||||
@@ -163,7 +170,7 @@ def setColorTemperature(kelvin) {
|
||||
def on() {
|
||||
log.debug "Device setOn"
|
||||
parent.logErrors() {
|
||||
if (parent.apiPUT("/lights/${device.deviceNetworkId}/power", [state: "on"]) != null) {
|
||||
if (parent.apiPUT("/lights/${selector()}/state", [power: "on"]) != null) {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
}
|
||||
@@ -172,7 +179,7 @@ def on() {
|
||||
def off() {
|
||||
log.debug "Device setOff"
|
||||
parent.logErrors() {
|
||||
if (parent.apiPUT("/lights/${device.deviceNetworkId}/power", [state: "off"]) != null) {
|
||||
if (parent.apiPUT("/lights/${selector()}/state", [power: "off"]) != null) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
}
|
||||
}
|
||||
@@ -180,19 +187,26 @@ def off() {
|
||||
|
||||
def poll() {
|
||||
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
|
||||
def resp = parent.apiGET("/lights/${device.deviceNetworkId}")
|
||||
if (resp.status != 200) {
|
||||
def resp = parent.apiGET("/lights/${selector()}")
|
||||
if (resp.status == 404) {
|
||||
sendEvent(name: "switch", value: "unreachable")
|
||||
return []
|
||||
} else if (resp.status != 200) {
|
||||
log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
|
||||
return []
|
||||
}
|
||||
def data = resp.data
|
||||
def data = resp.data[0]
|
||||
log.debug("Data: ${data}")
|
||||
|
||||
sendEvent(name: "level", value: sprintf("%.1f", (data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "label", value: data.label)
|
||||
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
|
||||
sendEvent(name: "color", value: colorUtil.hslToHex((data.color.hue / 3.6) as int, (data.color.saturation * 100) as int))
|
||||
sendEvent(name: "hue", value: data.color.hue / 3.6)
|
||||
sendEvent(name: "hue", value: data.color.hue / 3.6)
|
||||
sendEvent(name: "saturation", value: data.color.saturation * 100)
|
||||
sendEvent(name: "colorTemperature", value: data.color.kelvin)
|
||||
sendEvent(name: "model", value: "${data.product.company} ${data.product.name}")
|
||||
|
||||
return []
|
||||
}
|
||||
@@ -201,3 +215,11 @@ def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
poll()
|
||||
}
|
||||
|
||||
def selector() {
|
||||
if (device.deviceNetworkId.contains(":")) {
|
||||
return device.deviceNetworkId
|
||||
} else {
|
||||
return "id:${device.deviceNetworkId}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,41 +16,44 @@ metadata {
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
|
||||
}
|
||||
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'Turning on', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOff", label:'Turning off', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "unreachable", label: "?", action:"refresh.refresh", icon:"st.switches.light.off", backgroundColor:"#666666"
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
valueTile("null", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:''
|
||||
}
|
||||
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false, range:"(0..100)") {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
valueTile("level", "device.level", inactiveLabel: false, icon: "st.illuminance.illuminance.light", decoration: "flat") {
|
||||
state "level", label: '${currentValue}%'
|
||||
}
|
||||
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 2, width: 4, inactiveLabel: false, range:"(2700..9000)") {
|
||||
state "colorTemp", action:"color temperature.setColorTemperature"
|
||||
}
|
||||
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", height: 2, width: 2) {
|
||||
state "colorTemp", label: '${currentValue}K'
|
||||
}
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "refresh", "level", "levelSliderControl", "colorTempSliderControl", "colorTemp"])
|
||||
main "switch"
|
||||
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
@@ -72,9 +75,10 @@ def setLevel(percentage) {
|
||||
return off() // if the brightness is set to 0, just turn it off
|
||||
}
|
||||
parent.logErrors(logObject:log) {
|
||||
def resp = parent.apiPUT("/lights/${device.deviceNetworkId}/color", ["color": "brightness:${percentage / 100}"])
|
||||
def resp = parent.apiPUT("/lights/${selector()}/state", [brightness: percentage / 100, power: "on"])
|
||||
if (resp.status < 300) {
|
||||
sendEvent(name: "level", value: percentage)
|
||||
sendEvent(name: "switch.setLevel", value: percentage)
|
||||
sendEvent(name: "switch", value: "on")
|
||||
} else {
|
||||
log.error("Bad setLevel result: [${resp.status}] ${resp.data}")
|
||||
@@ -85,7 +89,7 @@ def setLevel(percentage) {
|
||||
def setColorTemperature(kelvin) {
|
||||
log.debug "Executing 'setColorTemperature' to ${kelvin}"
|
||||
parent.logErrors() {
|
||||
def resp = parent.apiPUT("/lights/${device.deviceNetworkId}/color", [color: "kelvin:${kelvin}"])
|
||||
def resp = parent.apiPUT("/lights/${selector()}/state", [color: "kelvin:${kelvin}", power: "on"])
|
||||
if (resp.status < 300) {
|
||||
sendEvent(name: "colorTemperature", value: kelvin)
|
||||
sendEvent(name: "color", value: "#ffffff")
|
||||
@@ -100,7 +104,7 @@ def setColorTemperature(kelvin) {
|
||||
def on() {
|
||||
log.debug "Device setOn"
|
||||
parent.logErrors() {
|
||||
if (parent.apiPUT("/lights/${device.deviceNetworkId}/power", [state: "on"]) != null) {
|
||||
if (parent.apiPUT("/lights/${selector()}/state", [power: "on"]) != null) {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
}
|
||||
@@ -109,7 +113,7 @@ def on() {
|
||||
def off() {
|
||||
log.debug "Device setOff"
|
||||
parent.logErrors() {
|
||||
if (parent.apiPUT("/lights/${device.deviceNetworkId}/power", [state: "off"]) != null) {
|
||||
if (parent.apiPUT("/lights/${selector()}/state", [power: "off"]) != null) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
}
|
||||
}
|
||||
@@ -117,16 +121,22 @@ def off() {
|
||||
|
||||
def poll() {
|
||||
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
|
||||
def resp = parent.apiGET("/lights/${device.deviceNetworkId}")
|
||||
if (resp.status != 200) {
|
||||
def resp = parent.apiGET("/lights/${selector()}")
|
||||
if (resp.status == 404) {
|
||||
sendEvent(name: "switch", value: "unreachable")
|
||||
return []
|
||||
} else if (resp.status != 200) {
|
||||
log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
|
||||
return []
|
||||
}
|
||||
def data = resp.data
|
||||
def data = resp.data[0]
|
||||
|
||||
sendEvent(name: "level", value: sprintf("%f", (data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "label", value: data.label)
|
||||
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
|
||||
sendEvent(name: "colorTemperature", value: data.color.kelvin)
|
||||
sendEvent(name: "model", value: data.product.name)
|
||||
|
||||
return []
|
||||
}
|
||||
@@ -135,3 +145,11 @@ def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
poll()
|
||||
}
|
||||
|
||||
def selector() {
|
||||
if (device.deviceNetworkId.contains(":")) {
|
||||
return device.deviceNetworkId
|
||||
} else {
|
||||
return "id:${device.deviceNetworkId}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,8 +88,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ metadata {
|
||||
])
|
||||
}
|
||||
section {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ metadata {
|
||||
])
|
||||
}
|
||||
section {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -61,8 +61,8 @@
|
||||
])
|
||||
}
|
||||
section {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
section {
|
||||
input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false)
|
||||
|
||||
@@ -43,8 +43,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -34,8 +34,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -33,8 +33,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -45,8 +45,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles {
|
||||
|
||||
@@ -33,8 +33,8 @@ metadata {
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles {
|
||||
|
||||
@@ -25,8 +25,8 @@ metadata {
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-CIA19NAE26", deviceJoinName: "Sengled Element touch"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B05,0702", outClusters: "000A,0019", manufacturer: "Jasco Products", model: "45852", deviceJoinName: "GE Zigbee Plug-In Dimmer"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B05,0702", outClusters: "000A,0019", manufacturer: "Jasco Products", model: "45857", deviceJoinName: "GE Zigbee In-Wall Dimmer"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "000A, 0019", manufacturer: "Jasco Products", model: "45852", deviceJoinName: "GE Zigbee Plug-In Dimmer"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "000A, 0019", manufacturer: "Jasco Products", model: "45857", deviceJoinName: "GE Zigbee In-Wall Dimmer"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -17,15 +17,15 @@ metadata {
|
||||
capability "Actuator"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "OSRAM LIGHTIFY LED Smart Connected Light"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0001, 0300, 0006, 0008", outClusters: "0019", manufacturer: "OSRAM", model: "ZHA_LIGHTIFY_COLOR_A19", deviceJoinName: "OSRAM LIGHTIFY COLOR SMART A19"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
@@ -87,4 +87,4 @@ def refresh() {
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ metadata {
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B05,0702", outClusters: "0003, 000A,0019", manufacturer: "Jasco Products", model: "45853", deviceJoinName: "GE ZigBee Plug-In Switch"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B05,0702", outClusters: "000A,0019", manufacturer: "Jasco Products", model: "45856", deviceJoinName: "GE ZigBee In-Wall Switch"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B05", outClusters: "0003, 000A, 0019", manufacturer: "Jasco Products", model: "45853", deviceJoinName: "GE ZigBee Plug-In Switch"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B05", outClusters: "000A, 0019", manufacturer: "Jasco Products", model: "45856", deviceJoinName: "GE ZigBee In-Wall Switch"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -17,7 +17,6 @@ metadata {
|
||||
capability "Actuator"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Switch"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006"
|
||||
|
||||
@@ -23,18 +23,18 @@ metadata {
|
||||
capability "Color Temperature"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
|
||||
attribute "colorName", "string"
|
||||
command "setGenericName"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04", outClusters: "0019"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Flood BR30 Tunable White"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Recessed Kit RT 5/6 Tunable White"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04", outClusters: "0019"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Flood BR30 Tunable White"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Recessed Kit RT 5/6 Tunable White"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
|
||||
@@ -66,9 +66,20 @@ metadata {
|
||||
import physicalgraph.zwave.commands.doorlockv1.*
|
||||
import physicalgraph.zwave.commands.usercodev1.*
|
||||
|
||||
def updated() {
|
||||
try {
|
||||
if (!state.init) {
|
||||
state.init = true
|
||||
response(secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()]))
|
||||
}
|
||||
} catch (e) {
|
||||
log.warn "updated() threw $e"
|
||||
}
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = null
|
||||
if (description.startsWith("Err")) {
|
||||
if (description.startsWith("Err 106")) {
|
||||
if (state.sec) {
|
||||
result = createEvent(descriptionText:description, displayed:false)
|
||||
} else {
|
||||
@@ -80,6 +91,8 @@ def parse(String description) {
|
||||
displayed: true,
|
||||
)
|
||||
}
|
||||
} else if (description == "updated") {
|
||||
return null
|
||||
} else {
|
||||
def cmd = zwave.parse(description, [ 0x98: 1, 0x72: 2, 0x85: 2, 0x86: 1 ])
|
||||
if (cmd) {
|
||||
@@ -286,7 +299,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
|
||||
}
|
||||
break
|
||||
case 167:
|
||||
if (!state.lastbatt || (new Date().time) - state.lastbatt > 12*60*60*1000) {
|
||||
if (!state.lastbatt || now() - state.lastbatt > 12*60*60*1000) {
|
||||
map = [ descriptionText: "$device.displayName: battery low", isStateChange: true ]
|
||||
result << response(secure(zwave.batteryV1.batteryGet()))
|
||||
} else {
|
||||
@@ -431,7 +444,7 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
} else {
|
||||
map.value = cmd.batteryLevel
|
||||
}
|
||||
state.lastbatt = new Date().time
|
||||
state.lastbatt = now()
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
@@ -499,15 +512,14 @@ def refresh() {
|
||||
cmds << "delay 4200"
|
||||
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format() // old Schlage locks use group 2 and don't secure the Association CC
|
||||
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
|
||||
state.associationQuery = new Date().time
|
||||
} else if (new Date().time - state.associationQuery.toLong() > 9000) {
|
||||
log.debug "setting association"
|
||||
state.associationQuery = now()
|
||||
} else if (secondsPast(state.associationQuery, 9)) {
|
||||
cmds << "delay 6000"
|
||||
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
|
||||
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
|
||||
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
|
||||
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
|
||||
state.associationQuery = new Date().time
|
||||
state.associationQuery = now()
|
||||
}
|
||||
log.debug "refresh sending ${cmds.inspect()}"
|
||||
cmds
|
||||
@@ -515,55 +527,22 @@ def refresh() {
|
||||
|
||||
def poll() {
|
||||
def cmds = []
|
||||
if (state.assoc != zwaveHubNodeId && secondsPast(state.associationQuery, 19 * 60)) {
|
||||
log.debug "setting association"
|
||||
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
|
||||
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
|
||||
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
|
||||
cmds << "delay 6000"
|
||||
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
|
||||
cmds << "delay 6000"
|
||||
state.associationQuery = new Date().time
|
||||
} else {
|
||||
// Only check lock state if it changed recently or we haven't had an update in an hour
|
||||
def latest = device.currentState("lock")?.date?.time
|
||||
if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60)) {
|
||||
cmds << secure(zwave.doorLockV1.doorLockOperationGet())
|
||||
state.lastPoll = (new Date()).time
|
||||
} else if (!state.MSR) {
|
||||
cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
|
||||
} else if (!state.fw) {
|
||||
cmds << zwave.versionV1.versionGet().format()
|
||||
} else if (!state.codes) {
|
||||
state.pollCode = 1
|
||||
cmds << secure(zwave.userCodeV1.usersNumberGet())
|
||||
} else if (state.pollCode && state.pollCode <= state.codes) {
|
||||
cmds << requestCode(state.pollCode)
|
||||
} else if (!state.lastbatt || (new Date().time) - state.lastbatt > 53*60*60*1000) {
|
||||
cmds << secure(zwave.batteryV1.batteryGet())
|
||||
} else if (!state.enc) {
|
||||
encryptCodes()
|
||||
state.enc = 1
|
||||
}
|
||||
// Only check lock state if it changed recently or we haven't had an update in an hour
|
||||
def latest = device.currentState("lock")?.date?.time
|
||||
if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60)) {
|
||||
cmds << secure(zwave.doorLockV1.doorLockOperationGet())
|
||||
state.lastPoll = now()
|
||||
} else if (!state.lastbatt || now() - state.lastbatt > 53*60*60*1000) {
|
||||
cmds << secure(zwave.batteryV1.batteryGet())
|
||||
state.lastbatt = now() //inside-214
|
||||
}
|
||||
log.debug "poll is sending ${cmds.inspect()}"
|
||||
device.activity()
|
||||
cmds ?: null
|
||||
}
|
||||
|
||||
private def encryptCodes() {
|
||||
def keys = new ArrayList(state.keySet().findAll { it.startsWith("code") })
|
||||
keys.each { key ->
|
||||
def match = (key =~ /^code(\d+)$/)
|
||||
if (match) try {
|
||||
def keynum = match[0][1].toInteger()
|
||||
if (keynum > 30 && !state[key]) {
|
||||
state.remove(key)
|
||||
} else if (state[key] && !state[key].startsWith("~")) {
|
||||
log.debug "encrypting $key: ${state[key].inspect()}"
|
||||
state[key] = encrypt(state[key])
|
||||
}
|
||||
} catch (java.lang.NumberFormatException e) { }
|
||||
if (cmds) {
|
||||
log.debug "poll is sending ${cmds.inspect()}"
|
||||
cmds
|
||||
} else {
|
||||
// workaround to keep polling from stopping due to lack of activity
|
||||
sendEvent(descriptionText: "skipping poll", isStateChange: true, displayed: false)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,7 +651,7 @@ private Boolean secondsPast(timestamp, seconds) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return (new Date().time - timestamp) > (seconds * 1000)
|
||||
return (now() - timestamp) > (seconds * 1000)
|
||||
}
|
||||
|
||||
private allCodesDeleted() {
|
||||
|
||||
@@ -246,6 +246,9 @@ def toggle(devices) {
|
||||
else if (devices*.currentValue('lock').contains('locked')) {
|
||||
devices.unlock()
|
||||
}
|
||||
else if (devices*.currentValue('lock').contains('unlocked')) {
|
||||
devices.lock()
|
||||
}
|
||||
else if (devices*.currentValue('alarm').contains('off')) {
|
||||
devices.siren()
|
||||
}
|
||||
|
||||
@@ -592,7 +592,7 @@ def updated() {
|
||||
// log.debug "External Id=${app.id}:${member.id}"
|
||||
|
||||
// create the device
|
||||
def childDevice = addChildDevice("smartthings", "life360-user", "${app.id}.${member.id}",null,[name:member.firstName, completedSetup: true])
|
||||
def childDevice = addChildDevice("smartthings", "Life360 User", "${app.id}.${member.id}",null,[name:member.firstName, completedSetup: true])
|
||||
// childDevice.setMemberId(member.id)
|
||||
|
||||
if (childDevice)
|
||||
|
||||
@@ -5,23 +5,23 @@
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "LIFX (Connect)",
|
||||
namespace: "smartthings",
|
||||
author: "LIFX",
|
||||
description: "Allows you to use LIFX smart light bulbs with SmartThings.",
|
||||
category: "Convenience",
|
||||
iconUrl: "https://cloud.lifx.com/images/lifx.png",
|
||||
iconX2Url: "https://cloud.lifx.com/images/lifx.png",
|
||||
iconX3Url: "https://cloud.lifx.com/images/lifx.png",
|
||||
oauth: true,
|
||||
singleInstance: true) {
|
||||
appSetting "clientId"
|
||||
appSetting "clientSecret"
|
||||
}
|
||||
name: "LIFX (Connect)",
|
||||
namespace: "smartthings",
|
||||
author: "LIFX",
|
||||
description: "Allows you to use LIFX smart light bulbs with SmartThings.",
|
||||
category: "Convenience",
|
||||
iconUrl: "https://cloud.lifx.com/images/lifx.png",
|
||||
iconX2Url: "https://cloud.lifx.com/images/lifx.png",
|
||||
iconX3Url: "https://cloud.lifx.com/images/lifx.png",
|
||||
oauth: true,
|
||||
singleInstance: true) {
|
||||
appSetting "clientId"
|
||||
appSetting "clientSecret"
|
||||
}
|
||||
|
||||
|
||||
preferences {
|
||||
page(name: "Credentials", title: "LIFX", content: "authPage", install: false)
|
||||
page(name: "Credentials", title: "LIFX", content: "authPage", install: true)
|
||||
}
|
||||
|
||||
mappings {
|
||||
@@ -33,29 +33,29 @@ mappings {
|
||||
path("/test") { action: [ GET: "oauthSuccess" ] }
|
||||
}
|
||||
|
||||
def getServerUrl() { return "https://graph.api.smartthings.com" }
|
||||
def apiURL(path = '/') { return "https://api.lifx.com/v1beta1${path}" }
|
||||
def buildRedirectUrl(page) {
|
||||
return "${serverUrl}/api/token/${state.accessToken}/smartapps/installations/${app.id}/${page}"
|
||||
}
|
||||
def getServerUrl() { return "https://graph.api.smartthings.com" }
|
||||
def getCallbackUrl() { return "https://graph.api.smartthings.com/oauth/callback"}
|
||||
def apiURL(path = '/') { return "https://api.lifx.com/v1${path}" }
|
||||
def getSecretKey() { return appSettings.secretKey }
|
||||
def getClientId() { return appSettings.clientId }
|
||||
|
||||
def authPage() {
|
||||
log.debug "authPage"
|
||||
log.debug "authPage test1"
|
||||
if (!state.lifxAccessToken) {
|
||||
log.debug "no LIFX access token"
|
||||
// This is the SmartThings access token
|
||||
if (!state.accessToken) {
|
||||
log.debug "no access token, create access token"
|
||||
createAccessToken() // predefined method
|
||||
state.accessToken = createAccessToken() // predefined method
|
||||
}
|
||||
def description = "Tap to enter LIFX credentials"
|
||||
def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}" // this triggers oauthInit() below
|
||||
log.debug "app id: ${app.id}"
|
||||
def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" // this triggers oauthInit() below
|
||||
// def redirectUrl = "${apiServerUrl}"
|
||||
log.debug "app id: ${app.id}"
|
||||
log.debug "redirect url: ${redirectUrl}"
|
||||
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:false) {
|
||||
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:true) {
|
||||
section {
|
||||
href(url:redirectUrl, required:true, title:"Connect to LIFX", description:"Tap here to connect your LIFX account")
|
||||
// href(url:buildRedirectUrl("test"), title: "Message test")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -63,17 +63,15 @@ def authPage() {
|
||||
|
||||
def options = locationOptions() ?: []
|
||||
def count = options.size()
|
||||
def refreshInterval = 3
|
||||
|
||||
return dynamicPage(name:"Credentials", title:"Select devices...", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) {
|
||||
return dynamicPage(name:"Credentials", title:"", nextPage:"", install:true, uninstall: true) {
|
||||
section("Select your location") {
|
||||
input "selectedLocationId", "enum", required:true, title:"Select location (${count} found)", multiple:false, options:options
|
||||
input "selectedLocationId", "enum", required:true, title:"Select location (${count} found)", multiple:false, options:options, submitOnChange: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// OAuth
|
||||
|
||||
def oauthInit() {
|
||||
@@ -112,7 +110,7 @@ def oauthCallback() {
|
||||
}
|
||||
|
||||
def oauthReceiveToken(redirectUrl = null) {
|
||||
|
||||
// Not sure what redirectUrl is for
|
||||
log.debug "receiveToken - params: ${params}"
|
||||
def oauthParams = [ client_id: "${appSettings.clientId}", client_secret: "${appSettings.clientSecret}", grant_type: "authorization_code", code: params.code, scope: params.scope ] // how is params.code valid here?
|
||||
def params = [
|
||||
@@ -135,25 +133,25 @@ def oauthReceiveToken(redirectUrl = null) {
|
||||
|
||||
def oauthSuccess() {
|
||||
def message = """
|
||||
<p>Your LIFX Account is now connected to SmartThings!</p>
|
||||
<p>Click 'Done' to finish setup.</p>
|
||||
"""
|
||||
<p>Your LIFX Account is now connected to SmartThings!</p>
|
||||
<p>Click 'Done' to finish setup.</p>
|
||||
"""
|
||||
oauthConnectionStatus(message)
|
||||
}
|
||||
|
||||
def oauthFailure() {
|
||||
def message = """
|
||||
<p>The connection could not be established!</p>
|
||||
<p>Click 'Done' to return to the menu.</p>
|
||||
"""
|
||||
<p>The connection could not be established!</p>
|
||||
<p>Click 'Done' to return to the menu.</p>
|
||||
"""
|
||||
oauthConnectionStatus(message)
|
||||
}
|
||||
|
||||
def oauthReceivedToken() {
|
||||
def message = """
|
||||
<p>Your LIFX Account is already connected to SmartThings!</p>
|
||||
<p>Click 'Done' to finish setup.</p>
|
||||
"""
|
||||
<p>Your LIFX Account is already connected to SmartThings!</p>
|
||||
<p>Click 'Done' to finish setup.</p>
|
||||
"""
|
||||
oauthConnectionStatus(message)
|
||||
}
|
||||
|
||||
@@ -161,74 +159,74 @@ def oauthConnectionStatus(message, redirectUrl = null) {
|
||||
def redirectHtml = ""
|
||||
if (redirectUrl) {
|
||||
redirectHtml = """
|
||||
<meta http-equiv="refresh" content="3; url=${redirectUrl}" />
|
||||
"""
|
||||
<meta http-equiv="refresh" content="3; url=${redirectUrl}" />
|
||||
"""
|
||||
}
|
||||
|
||||
def html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>SmartThings Connection</title>
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.svg#swis721_th_btthin') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.svg#swis721_lt_btlight') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
.container {
|
||||
width: 280;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
img:nth-child(2) {
|
||||
margin: 0 15px;
|
||||
}
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
text-align: center;
|
||||
color: #666666;
|
||||
padding: 0 20px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
span {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
}
|
||||
</style>
|
||||
${redirectHtml}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src='https://cloud.lifx.com/images/lifx.png' alt='LIFX icon' width='100'/>
|
||||
<img src='https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png' alt='connected device icon' width="40"/>
|
||||
<img src='https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png' alt='SmartThings logo' width="100"/>
|
||||
<p>
|
||||
${message}
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>SmartThings Connection</title>
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.svg#swis721_th_btthin') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.svg#swis721_lt_btlight') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
.container {
|
||||
width: 280;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
img:nth-child(2) {
|
||||
margin: 0 15px;
|
||||
}
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
text-align: center;
|
||||
color: #666666;
|
||||
padding: 0 20px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
span {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
}
|
||||
</style>
|
||||
${redirectHtml}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src='https://cloud.lifx.com/images/lifx.png' alt='LIFX icon' width='100'/>
|
||||
<img src='https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png' alt='connected device icon' width="40"/>
|
||||
<img src='https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png' alt='SmartThings logo' width="100"/>
|
||||
<p>
|
||||
${message}
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
render contentType: 'text/html', data: html
|
||||
}
|
||||
|
||||
@@ -239,7 +237,6 @@ String toQueryString(Map m) {
|
||||
// App lifecycle hooks
|
||||
|
||||
def installed() {
|
||||
enableCallback() // wtf does this do?
|
||||
if (!state.accessToken) {
|
||||
createAccessToken()
|
||||
} else {
|
||||
@@ -251,7 +248,6 @@ def installed() {
|
||||
|
||||
// called after settings are changed
|
||||
def updated() {
|
||||
enableCallback() // not sure what this does
|
||||
if (!state.accessToken) {
|
||||
createAccessToken()
|
||||
} else {
|
||||
@@ -305,27 +301,36 @@ def logErrors(options = [errorReturn: null, logObject: log], Closure c) {
|
||||
state.remove("lifxAccessToken")
|
||||
options.logObject.warn "Access token is not valid"
|
||||
}
|
||||
return options.errerReturn
|
||||
return options.errorReturn
|
||||
} catch (java.net.SocketTimeoutException e) {
|
||||
options.logObject.warn "Connection timed out, not much we can do here"
|
||||
return options.errerReturn
|
||||
return options.errorReturn
|
||||
}
|
||||
}
|
||||
|
||||
def apiGET(path) {
|
||||
httpGet(uri: apiURL(path), headers: apiRequestHeaders()) {response ->
|
||||
logResponse(response)
|
||||
return response
|
||||
try {
|
||||
httpGet(uri: apiURL(path), headers: apiRequestHeaders()) {response ->
|
||||
logResponse(response)
|
||||
return response
|
||||
}
|
||||
} catch (groovyx.net.http.HttpResponseException e) {
|
||||
logResponse(e.response)
|
||||
return e.response
|
||||
}
|
||||
}
|
||||
|
||||
def apiPUT(path, body = [:]) {
|
||||
log.debug("Beginning API PUT: ${path}, ${body}")
|
||||
httpPutJson(uri: apiURL(path), body: new groovy.json.JsonBuilder(body).toString(), headers: apiRequestHeaders(), ) {response ->
|
||||
logResponse(response)
|
||||
return response
|
||||
}
|
||||
}
|
||||
try {
|
||||
log.debug("Beginning API PUT: ${path}, ${body}")
|
||||
httpPutJson(uri: apiURL(path), body: new groovy.json.JsonBuilder(body).toString(), headers: apiRequestHeaders(), ) {response ->
|
||||
logResponse(response)
|
||||
return response
|
||||
}
|
||||
} catch (groovyx.net.http.HttpResponseException e) {
|
||||
logResponse(e.response)
|
||||
return e.response
|
||||
}}
|
||||
|
||||
def devicesList(selector = '') {
|
||||
logErrors([]) {
|
||||
@@ -340,12 +345,12 @@ def devicesList(selector = '') {
|
||||
}
|
||||
|
||||
Map locationOptions() {
|
||||
|
||||
def options = [:]
|
||||
def devices = devicesList()
|
||||
devices.each { device ->
|
||||
options[device.location.id] = device.location.name
|
||||
}
|
||||
log.debug("Locations: ${options}")
|
||||
return options
|
||||
}
|
||||
|
||||
@@ -359,28 +364,32 @@ def updateDevices() {
|
||||
state.devices = [:]
|
||||
}
|
||||
def devices = devicesInLocation()
|
||||
def deviceIds = devices*.id
|
||||
def selectors = []
|
||||
|
||||
log.debug("All selectors: ${selectors}")
|
||||
|
||||
devices.each { device ->
|
||||
def childDevice = getChildDevice(device.id)
|
||||
selectors.add("${device.id}")
|
||||
if (!childDevice) {
|
||||
log.info("Adding device ${device.id}: ${device.capabilities}")
|
||||
log.info("Adding device ${device.id}: ${device.product}")
|
||||
def data = [
|
||||
label: device.label,
|
||||
level: sprintf("%f", (device.brightness ?: 1) * 100),
|
||||
level: Math.round((device.brightness ?: 1) * 100),
|
||||
switch: device.connected ? device.power : "unreachable",
|
||||
colorTemperature: device.color.kelvin
|
||||
]
|
||||
if (device.capabilities.has_color) {
|
||||
if (device.product.capabilities.has_color) {
|
||||
data["color"] = colorUtil.hslToHex((device.color.hue / 3.6) as int, (device.color.saturation * 100) as int)
|
||||
data["hue"] = device.color.hue / 3.6
|
||||
data["saturation"] = device.color.saturation * 100
|
||||
childDevice = addChildDevice("smartthings", "LIFX Color Bulb", device.id, null, data)
|
||||
childDevice = addChildDevice(app.namespace, "LIFX Color Bulb", device.id, null, data)
|
||||
} else {
|
||||
childDevice = addChildDevice("smartthings", "LIFX White Bulb", device.id, null, data)
|
||||
childDevice = addChildDevice(app.namespace, "LIFX White Bulb", device.id, null, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
getChildDevices().findAll { !deviceIds.contains(it.deviceNetworkId) }.each {
|
||||
getChildDevices().findAll { !selectors.contains("${it.deviceNetworkId}") }.each {
|
||||
log.info("Deleting ${it.deviceNetworkId}")
|
||||
deleteChildDevice(it.deviceNetworkId)
|
||||
}
|
||||
@@ -392,4 +401,4 @@ def refreshDevices() {
|
||||
getChildDevices().each { device ->
|
||||
device.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,24 +10,24 @@
|
||||
* 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.
|
||||
*
|
||||
* Sonos Control
|
||||
* Speaker Control
|
||||
*
|
||||
* Author: SmartThings
|
||||
*
|
||||
* Date: 2013-12-10
|
||||
*/
|
||||
definition(
|
||||
name: "Sonos Control",
|
||||
name: "Speaker Control",
|
||||
namespace: "smartthings",
|
||||
author: "SmartThings",
|
||||
description: "Play or pause your Sonos when certain actions take place in your home.",
|
||||
description: "Play or pause your Speaker when certain actions take place in your home.",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Control your Sonos when something happens", install: true, uninstall: true)
|
||||
page(name: "mainPage", title: "Control your Speaker when something happens", install: true, uninstall: true)
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
input "starting", "time", title: "Starting", required: false
|
||||
@@ -81,7 +81,7 @@ def mainPage() {
|
||||
]
|
||||
}
|
||||
section {
|
||||
input "sonos", "capability.musicPlayer", title: "Sonos music player", required: true
|
||||
input "sonos", "capability.musicPlayer", title: "Speaker music player", required: true
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "volume", "number", title: "Set the volume volume", description: "0-100%", required: false
|
||||
@@ -10,7 +10,7 @@
|
||||
* 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.
|
||||
*
|
||||
* Sonos Mood Music
|
||||
* Speaker Mood Music
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2014-02-12
|
||||
@@ -65,7 +65,7 @@ private saveSelectedSong() {
|
||||
}
|
||||
|
||||
definition(
|
||||
name: "Sonos Mood Music",
|
||||
name: "Speaker Mood Music",
|
||||
namespace: "smartthings",
|
||||
author: "SmartThings",
|
||||
description: "Plays a selected song or station.",
|
||||
@@ -75,7 +75,7 @@ definition(
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Play a selected song or station on your Sonos when something happens", nextPage: "chooseTrack", uninstall: true)
|
||||
page(name: "mainPage", title: "Play a selected song or station on your Speaker when something happens", nextPage: "chooseTrack", uninstall: true)
|
||||
page(name: "chooseTrack", title: "Select a song", install: true)
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
@@ -125,7 +125,7 @@ def mainPage() {
|
||||
ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
section {
|
||||
input "sonos", "capability.musicPlayer", title: "On this Sonos player", required: true
|
||||
input "sonos", "capability.musicPlayer", title: "On this Speaker player", required: true
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "volume", "number", title: "Set the volume", description: "0-100%", required: false
|
||||
@@ -10,23 +10,23 @@
|
||||
* 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.
|
||||
*
|
||||
* Sonos Custom Message
|
||||
* Speaker Custom Message
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2014-1-29
|
||||
*/
|
||||
definition(
|
||||
name: "Sonos Notify with Sound",
|
||||
name: "Speaker Notify with Sound",
|
||||
namespace: "smartthings",
|
||||
author: "SmartThings",
|
||||
description: "Play a sound or custom message through your Sonos when the mode changes or other events occur.",
|
||||
description: "Play a sound or custom message through your Speaker when the mode changes or other events occur.",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Play a message on your Sonos when something happens", install: true, uninstall: true)
|
||||
page(name: "mainPage", title: "Play a message on your Speaker when something happens", install: true, uninstall: true)
|
||||
page(name: "chooseTrack", title: "Select a song or station")
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
@@ -92,7 +92,7 @@ def mainPage() {
|
||||
input "message","text",title:"Play this message", required:false, multiple: false
|
||||
}
|
||||
section {
|
||||
input "sonos", "capability.musicPlayer", title: "On this Sonos player", required: true
|
||||
input "sonos", "capability.musicPlayer", title: "On this Speaker player", required: true
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "resumePlaying", "bool", title: "Resume currently playing music after notification", required: false, defaultValue: true
|
||||
@@ -10,23 +10,23 @@
|
||||
* 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.
|
||||
*
|
||||
* Sonos Weather Forecast
|
||||
* Speaker Weather Forecast
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2014-1-29
|
||||
*/
|
||||
definition(
|
||||
name: "Sonos Weather Forecast",
|
||||
name: "Speaker Weather Forecast",
|
||||
namespace: "smartthings",
|
||||
author: "SmartThings",
|
||||
description: "Play a weather report through your Sonos when the mode changes or other events occur",
|
||||
description: "Play a weather report through your Speaker when the mode changes or other events occur",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Play the weather report on your sonos", install: true, uninstall: true)
|
||||
page(name: "mainPage", title: "Play the weather report on your speaker", install: true, uninstall: true)
|
||||
page(name: "chooseTrack", title: "Select a song or station")
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
@@ -85,7 +85,7 @@ def mainPage() {
|
||||
)
|
||||
}
|
||||
section {
|
||||
input "sonos", "capability.musicPlayer", title: "On this Sonos player", required: true
|
||||
input "sonos", "capability.musicPlayer", title: "On this Speaker player", required: true
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "resumePlaying", "bool", title: "Resume currently playing music after weather report finishes", required: false, defaultValue: true
|
||||
@@ -78,7 +78,7 @@ def firstPage()
|
||||
def motionsDiscovered = motionsDiscovered()
|
||||
def lightSwitchesDiscovered = lightSwitchesDiscovered()
|
||||
|
||||
return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: selectedSwitches != null || selectedMotions != null || selectedLightSwitches != null) {
|
||||
return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: true) {
|
||||
section("Select a device...") {
|
||||
input "selectedSwitches", "enum", required:false, title:"Select Wemo Switches \n(${switchesDiscovered.size() ?: 0} found)", multiple:true, options:switchesDiscovered
|
||||
input "selectedMotions", "enum", required:false, title:"Select Wemo Motions \n(${motionsDiscovered.size() ?: 0} found)", multiple:true, options:motionsDiscovered
|
||||
|
||||
Reference in New Issue
Block a user