Compare commits

...

34 Commits

Author SHA1 Message Date
Vinay Rao
01a36696d8 Merge pull request #291 from kwarodom/LiFXProd
LiFX - LiFX: update oauth/callback url using getApiServerUrl() for shard proxying and change shardUrl param to apiServerUrl
2015-11-17 15:00:30 -08:00
Yaima Valdivia
797def2935 Merge branch 'master' of github.com:SmartThingsCommunity/SmartThingsPublic 2015-11-17 10:40:04 -08:00
Yaima Valdivia
1034cd06e6 Var names reversed 2015-11-17 10:38:01 -08:00
Vinay Rao
c37729242e temporary changes to the lock DTH to account for the upcoming zigbee library changes 2015-11-17 10:38:01 -08:00
Vinay Rao
d29c3ec557 Cree DTH refresh 2015-11-17 10:38:01 -08:00
Vinay Rao
17be85b846 Moving re-certified device to generic zigbee DTH. 2015-11-17 10:38:01 -08:00
Vinay Rao
da06104563 Adding fingerprint for new bulbs and transition old color temperature osram bulbs to new one 2015-11-17 10:38:01 -08:00
Vinay Rao
5d37ac8515 new fingerprints for Yale. battery issue resolution for Yale 2015-11-17 10:38:01 -08:00
Vinay Rao
2a739fda07 adding fingerprints for osram and sengled dimmable bulbs 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
f627fb4fac Reverted to previously working getBridgeIP() 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
1d30a718b2 Replaced atomicState with State 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
303ca7117c Added a "," 2015-11-17 10:38:01 -08:00
Juan Pablo Risso
1c96645b9f removed function ShardUrl() 2015-11-17 10:38:00 -08:00
Juan Pablo Risso
af4dc0640a added singleInstance: true 2015-11-17 10:38:00 -08:00
juano2310
80b46153dc Harmony Global Oauth 2015-11-17 10:38:00 -08:00
Mike Robinet
b92cd9c637 CREX-1094 Delete stale device subscriptions on IFTTT app update 2015-11-17 10:38:00 -08:00
Juan Pablo Risso
1039b65c81 Code Cleanup 2015-11-17 10:38:00 -08:00
juano2310
4e203e8e13 buildActionUrl("hookCallback") 2015-11-17 10:38:00 -08:00
juano2310
61398105d1 Jawbone Global Oauth 2015-11-17 10:38:00 -08:00
bflorian
505efc5463 Deleted lights on when door opens after sundown 2015-11-17 10:38:00 -08:00
bflorian
3ddc82f996 More name/namespace changes 2015-11-17 10:38:00 -08:00
Juan Pablo Risso
13a324069d Force Level = 1% to 1
This ensures that the bulb will be able to dim to it minimum
2015-11-17 10:38:00 -08:00
bflorian
2fd5859326 Misc filename and namespace changes. 2015-11-17 10:38:00 -08:00
bflorian
bbedbddf9d Filename corrections 2015-11-17 10:38:00 -08:00
bflorian
e424e7abdd Corrected filename of Z-Wave Device Multichannel 2015-11-17 10:38:00 -08:00
Tom Manley
47fbdabf6b Fix 'Low Battery Handler' exception caused by non-integer battery events
ZigBee locks report battery percentage remaining in .5% increments. However
the Low Battery Handler Smart App in Hello Home expects it to be an integer.
2015-11-17 10:37:59 -08:00
Mike Robinet
7defe1cc61 CREX-3129 Update parent and service manager apps to be singleton 2015-11-17 10:37:59 -08:00
Mike Cousins
a78459347b update keen home smart vent device handler 2015-11-17 10:37:59 -08:00
juano2310
c473745e47 Wemo refactor final (DVCSMP-1189)
https://smartthings.atlassian.net/browse/DVCSMP-1189

Detect and mark device offline within 5 minutes.
Show Device offline in device tile.
Show Device offline in Recent Activity.
Log the current IP address to Recent Activity.
Log the changed IP address to Recent Activity.
Support 'Turning on' and 'Turning off' (blindly changing the state of
device to ON or OFF without confirming bulb responded correctly)
Turn on / off through Wemo-App reflected timely in SmartThings
App/Ecosystem.
Manual turn on / off of device is reflected timely in SmartThings
App/Ecosystem.

Lower case createEvent

Bug Fixes

Bug fixes

setOffline

Minor cosmetic fixes
2015-11-17 10:37:59 -08:00
Juan Pablo Risso
fc587ef15a Fix to Hue reverts dimmer settings (DVCSMP-1227)
if you use the hue native app to adjust the dimmer setting, smartthings will reset the dimmer to previous value when toggling from ST app (and automations)
2015-11-17 10:37:59 -08:00
Warodom Khamphanchai
0f3b730f26 DVCSMP-668
- Show batteryStatus tile insteady of battery tile to be able to display both when sensor is USB powered or battery powered
- Remove background for illuminance. This can be added when we have best practice of showing colors for lux.
- Instead of using powerSupply:failed, configurationGet cmd is sent and then the configure() is triggered by configurationReport to determine powerSupply (USB Cable/Battery)
- Instead of querying battery level on wake up, battery report is put in association group 2 that is configured to report every 6 hours by default
- Update configure() to  send both unsecure and secure configuration commands when sensor is joined normally or securely
2015-11-17 10:37:59 -08:00
Warodom Khamphanchai
11df2f31b3 DVCSMP-668
The following changes has been made to the original Aeon Multisensor device type handler to improve and modernize it:
1. Add "powerSupply" attribute to be able to tell power source (USB Cable/Battery)
2. Add preference page for user to customize "motion delay time", "motion sensitivity", and "sensor report interval"
3. Add color backgroud of "illuminance" value tile
4. Add tile for "ultravioletIndex"
5. Add tile for "powerSupply"
6. Modify updated() to be able to send configuration commands to sensor whether it is powered by USB cable or battery
7. When battery operated, send command to get update battery level if it hasn't been reported for a while
8. Report MSR of the sensor
9. Add handle for ConfigurationReport command class to update the "powerSupply" tile and change opetion mode of the sensor accordingly
10. Update configure() to configure parameters changed by user in preference page
11. Take out the "Configure" tile and instead send the configuration commands on every wakeup (sensor is battey powered)
2015-11-17 10:37:59 -08:00
Warodom Khamphanchai
5e93bca030 LiFX - change shardUrl param to apiServerUrl 2015-11-16 16:33:07 -08:00
Yaima Valdivia
4d243bf44d Rename Sonos SmartApps
https://smartthings.atlassian.net/browse/DVCSMP-607
2015-10-27 12:29:05 -07:00
7 changed files with 258 additions and 209 deletions

View File

@@ -17,44 +17,50 @@ metadata {
} }
simulator { simulator {
// TODO: define status and reply messages here
} }
tiles { tiles(scale: 2) {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
state "unreachable", label: "?", action:"refresh.refresh", icon:"st.switches.light.off", backgroundColor:"#666666" tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
state "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff" attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
state "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOn", label:'Turning on', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff" attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOff", label:'Turning off', action:"switch.on", icon:"st.switches.light.off", 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" state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
valueTile("null", "device.switch", inactiveLabel: false, decoration: "flat") { valueTile("null", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'' state "default", label:''
} }
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) { controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 2, width: 4, inactiveLabel: false, range:"(2700..9000)") {
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)") {
state "colorTemp", action:"color temperature.setColorTemperature" 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' state "colorTemp", label: '${currentValue}K'
} }
main(["switch"]) main "switch"
details(["switch", "refresh", "level", "levelSliderControl", "rgbSelector", "colorTempSliderControl", "colorTemp"]) details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
} }
} }
@@ -70,7 +76,7 @@ def parse(String description) {
def setHue(percentage) { def setHue(percentage) {
log.debug "setHue ${percentage}" log.debug "setHue ${percentage}"
parent.logErrors(logObject: log) { 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) { if (resp.status < 300) {
sendEvent(name: "hue", value: percentage) sendEvent(name: "hue", value: percentage)
sendEvent(name: "switch", value: "on") sendEvent(name: "switch", value: "on")
@@ -83,7 +89,7 @@ def setHue(percentage) {
def setSaturation(percentage) { def setSaturation(percentage) {
log.debug "setSaturation ${percentage}" log.debug "setSaturation ${percentage}"
parent.logErrors(logObject: log) { 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) { if (resp.status < 300) {
sendEvent(name: "saturation", value: percentage) sendEvent(name: "saturation", value: percentage)
sendEvent(name: "switch", value: "on") sendEvent(name: "switch", value: "on")
@@ -114,7 +120,7 @@ def setColor(Map color) {
} }
} }
parent.logErrors(logObject:log) { 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) { if (resp.status < 300) {
sendEvent(name: "color", value: color.hex) sendEvent(name: "color", value: color.hex)
sendEvent(name: "switch", value: "on") 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 return off() // if the brightness is set to 0, just turn it off
} }
parent.logErrors(logObject:log) { 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) { if (resp.status < 300) {
sendEvent(name: "level", value: percentage) sendEvent(name: "level", value: percentage)
sendEvent(name: "switch.setLevel", value: percentage)
sendEvent(name: "switch", value: "on") sendEvent(name: "switch", value: "on")
} else { } else {
log.error("Bad setLevel result: [${resp.status}] ${resp.data}") log.error("Bad setLevel result: [${resp.status}] ${resp.data}")
@@ -148,7 +155,7 @@ def setLevel(percentage) {
def setColorTemperature(kelvin) { def setColorTemperature(kelvin) {
log.debug "Executing 'setColorTemperature' to ${kelvin}" log.debug "Executing 'setColorTemperature' to ${kelvin}"
parent.logErrors() { 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) { if (resp.status < 300) {
sendEvent(name: "colorTemperature", value: kelvin) sendEvent(name: "colorTemperature", value: kelvin)
sendEvent(name: "color", value: "#ffffff") sendEvent(name: "color", value: "#ffffff")
@@ -163,7 +170,7 @@ def setColorTemperature(kelvin) {
def on() { def on() {
log.debug "Device setOn" log.debug "Device setOn"
parent.logErrors() { 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") sendEvent(name: "switch", value: "on")
} }
} }
@@ -172,7 +179,7 @@ def on() {
def off() { def off() {
log.debug "Device setOff" log.debug "Device setOff"
parent.logErrors() { 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") sendEvent(name: "switch", value: "off")
} }
} }
@@ -180,19 +187,26 @@ def off() {
def poll() { def poll() {
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}" log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
def resp = parent.apiGET("/lights/${device.deviceNetworkId}") def resp = parent.apiGET("/lights/${selector()}")
if (resp.status != 200) { 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}") log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
return [] 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: "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: "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: "saturation", value: data.color.saturation * 100)
sendEvent(name: "colorTemperature", value: data.color.kelvin) sendEvent(name: "colorTemperature", value: data.color.kelvin)
sendEvent(name: "model", value: "${data.product.company} ${data.product.name}")
return [] return []
} }
@@ -201,3 +215,11 @@ def refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
poll() poll()
} }
def selector() {
if (device.deviceNetworkId.contains(":")) {
return device.deviceNetworkId
} else {
return "id:${device.deviceNetworkId}"
}
}

View File

@@ -16,41 +16,44 @@ metadata {
} }
simulator { simulator {
// TODO: define status and reply messages here
} }
tiles { tiles(scale: 2) {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
state "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff" tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
state "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
state "turningOn", label:'Turning on', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff" attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'Turning off', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
state "unreachable", label: "?", action:"refresh.refresh", icon:"st.switches.light.off", backgroundColor:"#666666" 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" state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
valueTile("null", "device.switch", inactiveLabel: false, decoration: "flat") { valueTile("null", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'' state "default", label:''
} }
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false, range:"(0..100)") { controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 2, width: 4, inactiveLabel: false, range:"(2700..9000)") {
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)") {
state "colorTemp", action:"color temperature.setColorTemperature" 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' state "colorTemp", label: '${currentValue}K'
} }
main(["switch"]) main "switch"
details(["switch", "refresh", "level", "levelSliderControl", "colorTempSliderControl", "colorTemp"]) details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
} }
} }
// parse events into attributes // parse events into attributes
@@ -72,9 +75,10 @@ def setLevel(percentage) {
return off() // if the brightness is set to 0, just turn it off return off() // if the brightness is set to 0, just turn it off
} }
parent.logErrors(logObject:log) { 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) { if (resp.status < 300) {
sendEvent(name: "level", value: percentage) sendEvent(name: "level", value: percentage)
sendEvent(name: "switch.setLevel", value: percentage)
sendEvent(name: "switch", value: "on") sendEvent(name: "switch", value: "on")
} else { } else {
log.error("Bad setLevel result: [${resp.status}] ${resp.data}") log.error("Bad setLevel result: [${resp.status}] ${resp.data}")
@@ -85,7 +89,7 @@ def setLevel(percentage) {
def setColorTemperature(kelvin) { def setColorTemperature(kelvin) {
log.debug "Executing 'setColorTemperature' to ${kelvin}" log.debug "Executing 'setColorTemperature' to ${kelvin}"
parent.logErrors() { 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) { if (resp.status < 300) {
sendEvent(name: "colorTemperature", value: kelvin) sendEvent(name: "colorTemperature", value: kelvin)
sendEvent(name: "color", value: "#ffffff") sendEvent(name: "color", value: "#ffffff")
@@ -100,7 +104,7 @@ def setColorTemperature(kelvin) {
def on() { def on() {
log.debug "Device setOn" log.debug "Device setOn"
parent.logErrors() { 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") sendEvent(name: "switch", value: "on")
} }
} }
@@ -109,7 +113,7 @@ def on() {
def off() { def off() {
log.debug "Device setOff" log.debug "Device setOff"
parent.logErrors() { 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") sendEvent(name: "switch", value: "off")
} }
} }
@@ -117,16 +121,22 @@ def off() {
def poll() { def poll() {
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}" log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
def resp = parent.apiGET("/lights/${device.deviceNetworkId}") def resp = parent.apiGET("/lights/${selector()}")
if (resp.status != 200) { 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}") log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
return [] 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: "switch", value: data.connected ? data.power : "unreachable")
sendEvent(name: "colorTemperature", value: data.color.kelvin) sendEvent(name: "colorTemperature", value: data.color.kelvin)
sendEvent(name: "model", value: data.product.name)
return [] return []
} }
@@ -135,3 +145,11 @@ def refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
poll() poll()
} }
def selector() {
if (device.deviceNetworkId.contains(":")) {
return device.deviceNetworkId
} else {
return "id:${device.deviceNetworkId}"
}
}

View File

@@ -5,23 +5,23 @@
* *
*/ */
definition( definition(
name: "LIFX (Connect)", name: "LIFX (Connect)",
namespace: "smartthings", namespace: "smartthings",
author: "LIFX", author: "LIFX",
description: "Allows you to use LIFX smart light bulbs with SmartThings.", description: "Allows you to use LIFX smart light bulbs with SmartThings.",
category: "Convenience", category: "Convenience",
iconUrl: "https://cloud.lifx.com/images/lifx.png", iconUrl: "https://cloud.lifx.com/images/lifx.png",
iconX2Url: "https://cloud.lifx.com/images/lifx.png", iconX2Url: "https://cloud.lifx.com/images/lifx.png",
iconX3Url: "https://cloud.lifx.com/images/lifx.png", iconX3Url: "https://cloud.lifx.com/images/lifx.png",
oauth: true, oauth: true,
singleInstance: true) { singleInstance: true) {
appSetting "clientId" appSetting "clientId"
appSetting "clientSecret" appSetting "clientSecret"
} }
preferences { preferences {
page(name: "Credentials", title: "LIFX", content: "authPage", install: false) page(name: "Credentials", title: "LIFX", content: "authPage", install: true)
} }
mappings { mappings {
@@ -33,29 +33,29 @@ mappings {
path("/test") { action: [ GET: "oauthSuccess" ] } path("/test") { action: [ GET: "oauthSuccess" ] }
} }
def getServerUrl() { return "https://graph.api.smartthings.com" } def getServerUrl() { return "https://graph.api.smartthings.com" }
def apiURL(path = '/') { return "https://api.lifx.com/v1beta1${path}" } def getCallbackUrl() { return "https://graph.api.smartthings.com/oauth/callback"}
def buildRedirectUrl(page) { def apiURL(path = '/') { return "https://api.lifx.com/v1${path}" }
return "${serverUrl}/api/token/${state.accessToken}/smartapps/installations/${app.id}/${page}" def getSecretKey() { return appSettings.secretKey }
} def getClientId() { return appSettings.clientId }
def authPage() { def authPage() {
log.debug "authPage" log.debug "authPage test1"
if (!state.lifxAccessToken) { if (!state.lifxAccessToken) {
log.debug "no LIFX access token" log.debug "no LIFX access token"
// This is the SmartThings access token // This is the SmartThings access token
if (!state.accessToken) { if (!state.accessToken) {
log.debug "no access token, create access token" log.debug "no access token, create access token"
createAccessToken() // predefined method state.accessToken = createAccessToken() // predefined method
} }
def description = "Tap to enter LIFX credentials" def description = "Tap to enter LIFX credentials"
def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}" // this triggers oauthInit() below def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" // this triggers oauthInit() below
log.debug "app id: ${app.id}" // def redirectUrl = "${apiServerUrl}"
log.debug "app id: ${app.id}"
log.debug "redirect url: ${redirectUrl}" 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 { section {
href(url:redirectUrl, required:true, title:"Connect to LIFX", description:"Tap here to connect your LIFX account") 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 { } else {
@@ -63,17 +63,15 @@ def authPage() {
def options = locationOptions() ?: [] def options = locationOptions() ?: []
def count = options.size() 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") { 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 // OAuth
def oauthInit() { def oauthInit() {
@@ -112,7 +110,7 @@ def oauthCallback() {
} }
def oauthReceiveToken(redirectUrl = null) { def oauthReceiveToken(redirectUrl = null) {
// Not sure what redirectUrl is for
log.debug "receiveToken - params: ${params}" 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 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 = [ def params = [
@@ -135,25 +133,25 @@ def oauthReceiveToken(redirectUrl = null) {
def oauthSuccess() { def oauthSuccess() {
def message = """ def message = """
<p>Your LIFX Account is now connected to SmartThings!</p> <p>Your LIFX Account is now connected to SmartThings!</p>
<p>Click 'Done' to finish setup.</p> <p>Click 'Done' to finish setup.</p>
""" """
oauthConnectionStatus(message) oauthConnectionStatus(message)
} }
def oauthFailure() { def oauthFailure() {
def message = """ def message = """
<p>The connection could not be established!</p> <p>The connection could not be established!</p>
<p>Click 'Done' to return to the menu.</p> <p>Click 'Done' to return to the menu.</p>
""" """
oauthConnectionStatus(message) oauthConnectionStatus(message)
} }
def oauthReceivedToken() { def oauthReceivedToken() {
def message = """ def message = """
<p>Your LIFX Account is already connected to SmartThings!</p> <p>Your LIFX Account is already connected to SmartThings!</p>
<p>Click 'Done' to finish setup.</p> <p>Click 'Done' to finish setup.</p>
""" """
oauthConnectionStatus(message) oauthConnectionStatus(message)
} }
@@ -161,74 +159,74 @@ def oauthConnectionStatus(message, redirectUrl = null) {
def redirectHtml = "" def redirectHtml = ""
if (redirectUrl) { if (redirectUrl) {
redirectHtml = """ redirectHtml = """
<meta http-equiv="refresh" content="3; url=${redirectUrl}" /> <meta http-equiv="refresh" content="3; url=${redirectUrl}" />
""" """
} }
def html = """ def html = """
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<title>SmartThings Connection</title> <title>SmartThings Connection</title>
<style type="text/css"> <style type="text/css">
@font-face { @font-face {
font-family: 'Swiss 721 W01 Thin'; 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');
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot?#iefix') format('embedded-opentype'), 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.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.ttf') format('truetype'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.svg#swis721_th_btthin') format('svg'); url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.svg#swis721_th_btthin') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'Swiss 721 W01 Light'; 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');
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot?#iefix') format('embedded-opentype'), 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.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.ttf') format('truetype'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.svg#swis721_lt_btlight') format('svg'); url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.svg#swis721_lt_btlight') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
.container { .container {
width: 280; width: 280;
padding: 20px; padding: 20px;
text-align: center; text-align: center;
} }
img { img {
vertical-align: middle; vertical-align: middle;
} }
img:nth-child(2) { img:nth-child(2) {
margin: 0 15px; margin: 0 15px;
} }
p { p {
font-size: 1.2em; font-size: 1.2em;
font-family: 'Swiss 721 W01 Thin'; font-family: 'Swiss 721 W01 Thin';
text-align: center; text-align: center;
color: #666666; color: #666666;
padding: 0 20px; padding: 0 20px;
margin-bottom: 0; margin-bottom: 0;
} }
span { span {
font-family: 'Swiss 721 W01 Light'; font-family: 'Swiss 721 W01 Light';
} }
</style> </style>
${redirectHtml} ${redirectHtml}
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<img src='https://cloud.lifx.com/images/lifx.png' alt='LIFX icon' width='100'/> <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/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"/> <img src='https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png' alt='SmartThings logo' width="100"/>
<p> <p>
${message} ${message}
</p> </p>
</div> </div>
</body> </body>
</html> </html>
""" """
render contentType: 'text/html', data: html render contentType: 'text/html', data: html
} }
@@ -239,7 +237,6 @@ String toQueryString(Map m) {
// App lifecycle hooks // App lifecycle hooks
def installed() { def installed() {
enableCallback() // wtf does this do?
if (!state.accessToken) { if (!state.accessToken) {
createAccessToken() createAccessToken()
} else { } else {
@@ -251,7 +248,6 @@ def installed() {
// called after settings are changed // called after settings are changed
def updated() { def updated() {
enableCallback() // not sure what this does
if (!state.accessToken) { if (!state.accessToken) {
createAccessToken() createAccessToken()
} else { } else {
@@ -305,27 +301,36 @@ def logErrors(options = [errorReturn: null, logObject: log], Closure c) {
state.remove("lifxAccessToken") state.remove("lifxAccessToken")
options.logObject.warn "Access token is not valid" options.logObject.warn "Access token is not valid"
} }
return options.errerReturn return options.errorReturn
} catch (java.net.SocketTimeoutException e) { } catch (java.net.SocketTimeoutException e) {
options.logObject.warn "Connection timed out, not much we can do here" options.logObject.warn "Connection timed out, not much we can do here"
return options.errerReturn return options.errorReturn
} }
} }
def apiGET(path) { def apiGET(path) {
httpGet(uri: apiURL(path), headers: apiRequestHeaders()) {response -> try {
logResponse(response) httpGet(uri: apiURL(path), headers: apiRequestHeaders()) {response ->
return response logResponse(response)
return response
}
} catch (groovyx.net.http.HttpResponseException e) {
logResponse(e.response)
return e.response
} }
} }
def apiPUT(path, body = [:]) { def apiPUT(path, body = [:]) {
log.debug("Beginning API PUT: ${path}, ${body}") try {
httpPutJson(uri: apiURL(path), body: new groovy.json.JsonBuilder(body).toString(), headers: apiRequestHeaders(), ) {response -> log.debug("Beginning API PUT: ${path}, ${body}")
logResponse(response) httpPutJson(uri: apiURL(path), body: new groovy.json.JsonBuilder(body).toString(), headers: apiRequestHeaders(), ) {response ->
return response logResponse(response)
} return response
} }
} catch (groovyx.net.http.HttpResponseException e) {
logResponse(e.response)
return e.response
}}
def devicesList(selector = '') { def devicesList(selector = '') {
logErrors([]) { logErrors([]) {
@@ -340,12 +345,12 @@ def devicesList(selector = '') {
} }
Map locationOptions() { Map locationOptions() {
def options = [:] def options = [:]
def devices = devicesList() def devices = devicesList()
devices.each { device -> devices.each { device ->
options[device.location.id] = device.location.name options[device.location.id] = device.location.name
} }
log.debug("Locations: ${options}")
return options return options
} }
@@ -359,28 +364,32 @@ def updateDevices() {
state.devices = [:] state.devices = [:]
} }
def devices = devicesInLocation() def devices = devicesInLocation()
def deviceIds = devices*.id def selectors = []
log.debug("All selectors: ${selectors}")
devices.each { device -> devices.each { device ->
def childDevice = getChildDevice(device.id) def childDevice = getChildDevice(device.id)
selectors.add("${device.id}")
if (!childDevice) { if (!childDevice) {
log.info("Adding device ${device.id}: ${device.capabilities}") log.info("Adding device ${device.id}: ${device.product}")
def data = [ def data = [
label: device.label, label: device.label,
level: sprintf("%f", (device.brightness ?: 1) * 100), level: Math.round((device.brightness ?: 1) * 100),
switch: device.connected ? device.power : "unreachable", switch: device.connected ? device.power : "unreachable",
colorTemperature: device.color.kelvin 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["color"] = colorUtil.hslToHex((device.color.hue / 3.6) as int, (device.color.saturation * 100) as int)
data["hue"] = device.color.hue / 3.6 data["hue"] = device.color.hue / 3.6
data["saturation"] = device.color.saturation * 100 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 { } 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}") log.info("Deleting ${it.deviceNetworkId}")
deleteChildDevice(it.deviceNetworkId) deleteChildDevice(it.deviceNetworkId)
} }
@@ -392,4 +401,4 @@ def refreshDevices() {
getChildDevices().each { device -> getChildDevices().each { device ->
device.refresh() device.refresh()
} }
} }

View File

@@ -10,24 +10,24 @@
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
* Sonos Control * Speaker Control
* *
* Author: SmartThings * Author: SmartThings
* *
* Date: 2013-12-10 * Date: 2013-12-10
*/ */
definition( definition(
name: "Sonos Control", name: "Speaker Control",
namespace: "smartthings", namespace: "smartthings",
author: "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", category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png" iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
) )
preferences { 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") { page(name: "timeIntervalInput", title: "Only during a certain time") {
section { section {
input "starting", "time", title: "Starting", required: false input "starting", "time", title: "Starting", required: false
@@ -81,7 +81,7 @@ def mainPage() {
] ]
} }
section { 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) { section("More options", hideable: true, hidden: true) {
input "volume", "number", title: "Set the volume volume", description: "0-100%", required: false input "volume", "number", title: "Set the volume volume", description: "0-100%", required: false

View File

@@ -10,7 +10,7 @@
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
* Sonos Mood Music * Speaker Mood Music
* *
* Author: SmartThings * Author: SmartThings
* Date: 2014-02-12 * Date: 2014-02-12
@@ -65,7 +65,7 @@ private saveSelectedSong() {
} }
definition( definition(
name: "Sonos Mood Music", name: "Speaker Mood Music",
namespace: "smartthings", namespace: "smartthings",
author: "SmartThings", author: "SmartThings",
description: "Plays a selected song or station.", description: "Plays a selected song or station.",
@@ -75,7 +75,7 @@ definition(
) )
preferences { 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: "chooseTrack", title: "Select a song", install: true)
page(name: "timeIntervalInput", title: "Only during a certain time") { page(name: "timeIntervalInput", title: "Only during a certain time") {
section { section {
@@ -125,7 +125,7 @@ def mainPage() {
ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
} }
section { 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) { section("More options", hideable: true, hidden: true) {
input "volume", "number", title: "Set the volume", description: "0-100%", required: false input "volume", "number", title: "Set the volume", description: "0-100%", required: false

View File

@@ -10,23 +10,23 @@
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
* Sonos Custom Message * Speaker Custom Message
* *
* Author: SmartThings * Author: SmartThings
* Date: 2014-1-29 * Date: 2014-1-29
*/ */
definition( definition(
name: "Sonos Notify with Sound", name: "Speaker Notify with Sound",
namespace: "smartthings", namespace: "smartthings",
author: "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", category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png" iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
) )
preferences { 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: "chooseTrack", title: "Select a song or station")
page(name: "timeIntervalInput", title: "Only during a certain time") { page(name: "timeIntervalInput", title: "Only during a certain time") {
section { section {
@@ -92,7 +92,7 @@ def mainPage() {
input "message","text",title:"Play this message", required:false, multiple: false input "message","text",title:"Play this message", required:false, multiple: false
} }
section { 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) { section("More options", hideable: true, hidden: true) {
input "resumePlaying", "bool", title: "Resume currently playing music after notification", required: false, defaultValue: true input "resumePlaying", "bool", title: "Resume currently playing music after notification", required: false, defaultValue: true

View File

@@ -10,23 +10,23 @@
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
* Sonos Weather Forecast * Speaker Weather Forecast
* *
* Author: SmartThings * Author: SmartThings
* Date: 2014-1-29 * Date: 2014-1-29
*/ */
definition( definition(
name: "Sonos Weather Forecast", name: "Speaker Weather Forecast",
namespace: "smartthings", namespace: "smartthings",
author: "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", category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png" iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/sonos@2x.png"
) )
preferences { 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: "chooseTrack", title: "Select a song or station")
page(name: "timeIntervalInput", title: "Only during a certain time") { page(name: "timeIntervalInput", title: "Only during a certain time") {
section { section {
@@ -85,7 +85,7 @@ def mainPage() {
) )
} }
section { 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) { section("More options", hideable: true, hidden: true) {
input "resumePlaying", "bool", title: "Resume currently playing music after weather report finishes", required: false, defaultValue: true input "resumePlaying", "bool", title: "Resume currently playing music after weather report finishes", required: false, defaultValue: true