Compare commits

...

22 Commits

Author SHA1 Message Date
Terry wong
eaa5575ad2 MSA-1282: For testing purpose 2016-05-18 02:53:01 -05:00
Jim Anderson
feba6643a6 Merge pull request #893 from jimmyjames/tile-examples-default-state
DOCS-284 - update tiles for best practices, add setColor to colorWheel
2016-05-17 14:07:55 -05:00
Vinay Rao
49c893771d Merge pull request #905 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-05-17 10:06:48 -07:00
Vinay Rao
d73f4c2ded Merge pull request #904 from SmartThingsCommunity/revert-901-DVCSMP-1739-revert-untested-code
DVCSMP-1739 Revert "Undo DVCSMP-1739 Revert "make sure reformat the storeGraphData""
2016-05-17 10:01:27 -07:00
Vinay Rao
13a056ec8d Revert "Undo DVCSMP-1739 Revert "make sure reformat the storeGraphData"" 2016-05-17 09:59:03 -07:00
Lars Finander
f55452d1c6 Merge pull request #887 from larsfinander/Add_MSR_to_zwave-switch
DVCSMP-1770 Add MSR to zwave-switch and configure at pairing
2016-05-16 13:37:18 -07:00
rohandesai
14b5fd41b8 Merge pull request #895 from rohandesai/DVCSMP-1709
DVCSMP-1709 Android>SmartApps>PlatLinkConnector>Unable To Add
2016-05-16 13:18:22 -07:00
rohandesai
0f4d7bd520 Merge pull request #868 from rohandesai/DVCSMP-1699-life360
DVCSMP-1699 Na02>Missing/Failed Oauth Tokens - Life360
2016-05-16 13:18:13 -07:00
rohandesai
a95fbf2612 Merge pull request #867 from rohandesai/DVCSMP-1699
DVCSMP-1699 Na02>Missing/Failed Oauth Tokens - Netatmo
2016-05-16 13:17:57 -07:00
Jack Chi
0f60ca61cb Merge pull request #899 from jackchi/add-category-smartsense
[CHF-74] Add category to SmartSense DTH
2016-05-16 11:50:00 -07:00
jackchi
293a73136e [CHF-74] Add category to SmartSense DTH 2016-05-16 11:43:21 -07:00
Rohan Desai
8de3276ce6 DVCSMP-1709 Android>SmartApps>PlatLinkConnector>Unable To Add
- now getting the right shard url
2016-05-13 11:13:58 -07:00
Jim Anderson
f77c5810c6 DOCS-284 - update tiles for best practices, add setColor to colorWheel tile 2016-05-13 10:26:45 -05:00
Luke Bredeson
9fc5f14dd7 Merge pull request #235 from lbredeso/button-controller-dst-fix
SHARD-211: DST fix for Button Controller
2016-05-13 10:20:59 -05:00
Lars Finander
55d7a4a263 Merge pull request #886 from larsfinander/DVCSMP-1761_Remove_heartbeat
DVCSMP-1716 Remove heartbeat from device types
2016-05-12 21:01:17 -07:00
Rohan Desai
b33d621696 DVCSMP-1699 Na02>Missing/Failed Oauth Tokens
- now calling the method to get the right shard url for Life360
- removed serverUrl from the app settings
2016-05-12 11:25:36 -07:00
Rohan Desai
45b78eff8d DVCSMP-1699 Na02>Missing/Failed Oauth Tokens - Netatmo
- refactored the oauth component for the app to work in different shards, if supported
- fixed some white spacing issues and indents
- addressed comments
- edited spacing
2016-05-12 11:18:09 -07:00
Lars Finander
a133406b6e DVCSMP-1770 Add MSR to zwave-switch and configure at pairing
-Make ZWave switch and Dimmer switch behave the same and save MSR and
manufacturer name in data
2016-05-11 23:41:19 -07:00
Lars Finander
bd62962ee1 DVCSMP-1716 Remove heartbeat from device types
-Remove heartbeat from OSRAM white bulb and Smartpower Outlet
-Heartbeat was a hack previously used for Amazon Echo
2016-05-11 23:06:09 -07:00
Lars Finander
a5da182bf4 Merge pull request #861 from larsfinander/DVCSMP-1738_Phillips_HUE_brightness_level
DVCSMP-1738 Philips HUE: Detail page showing the Brightness level twice
2016-05-11 15:25:36 -07:00
Lars Finander
27c05f4e5b DVCSMP-1738 Philips HUE: Detail page showing the Brightness level twice
-Removed secondary control from all Hue DTH
2016-05-06 09:29:41 -07:00
Luke Bredeson
91c01dc643 DST fix for Button Controller 2015-10-29 12:35:28 -05:00
25 changed files with 294 additions and 343 deletions

View File

@@ -94,11 +94,11 @@ def parse(String description) {
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3]) def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3])
if (cmd) { if (cmd) {
result = createEvent(zwaveEvent(cmd)) result = createEvent(zwaveEvent(cmd))
log.debug "Parse returned ${result?.descriptionText}"
storeGraphData(result.name, result.value)
} else {
log.debug "zwave.parse returned null command. Cannot create event"
} }
log.debug "Parse returned ${result?.descriptionText}"
storeGraphData(result.name, result.value)
return result return result
} }

View File

@@ -138,6 +138,7 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
log.debug "productTypeId: ${cmd.productTypeId}" log.debug "productTypeId: ${cmd.productTypeId}"
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
updateDataValue("MSR", msr) updateDataValue("MSR", msr)
updateDataValue("manufacturer", cmd.manufacturerName)
createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
} }

View File

@@ -37,9 +37,6 @@ metadata {
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)" attributeState "level", action:"switch level.setLevel", range:"(0..100)"
} }
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
tileAttribute ("device.color", key: "COLOR_CONTROL") { tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor" attributeState "color", action:"setAdjustedColor"
} }

View File

@@ -38,9 +38,6 @@ metadata {
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)" attributeState "level", action:"switch level.setLevel", range:"(0..100)"
} }
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
tileAttribute ("device.color", key: "COLOR_CONTROL") { tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor" attributeState "color", action:"setAdjustedColor"
} }

View File

@@ -33,9 +33,6 @@ metadata {
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)" attributeState "level", action:"switch level.setLevel", range:"(0..100)"
} }
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
} }
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {

View File

@@ -19,11 +19,6 @@ metadata {
capability "Sensor" capability "Sensor"
attribute "colorName", "string" attribute "colorName", "string"
// indicates that device keeps track of heartbeat (in state.heartbeat)
attribute "heartbeat", "string"
} }
// simulator metadata // simulator metadata
@@ -75,9 +70,6 @@ metadata {
def parse(String description) { def parse(String description) {
//log.trace description //log.trace description
// save heartbeat (i.e. last time we got a message from device)
state.heartbeat = Calendar.getInstance().getTimeInMillis()
if (description?.startsWith("catchall:")) { if (description?.startsWith("catchall:")) {
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1")) if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
{ {
@@ -132,7 +124,6 @@ def off() {
} }
def refresh() { def refresh() {
sendEvent(name: "heartbeat", value: "alive", displayed:false)
[ [
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500", "st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500", "st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",

View File

@@ -16,7 +16,7 @@
metadata { metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings", category: "C1") {
capability "Actuator" capability "Actuator"
capability "Switch" capability "Switch"
capability "Power Meter" capability "Power Meter"
@@ -25,9 +25,6 @@ metadata {
capability "Sensor" capability "Sensor"
capability "Health Check" capability "Health Check"
// indicates that device keeps track of heartbeat (in state.heartbeat)
attribute "heartbeat", "string"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet"
@@ -81,9 +78,6 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
// save heartbeat (i.e. last time we got a message from device)
state.heartbeat = Calendar.getInstance().getTimeInMillis()
def finalResult = zigbee.getKnownDescription(description) def finalResult = zigbee.getKnownDescription(description)
//TODO: Remove this after getKnownDescription can parse it automatically //TODO: Remove this after getKnownDescription can parse it automatically
@@ -124,7 +118,6 @@ def on() {
} }
def refresh() { def refresh() {
sendEvent(name: "heartbeat", value: "alive", displayed:false)
zigbee.onOffRefresh() + zigbee.refreshData("0x0B04", "0x050B") zigbee.onOffRefresh() + zigbee.refreshData("0x0B04", "0x050B")
} }

View File

@@ -15,7 +15,7 @@
*/ */
metadata { metadata {
definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings", category: "C2") {
capability "Configuration" capability "Configuration"
capability "Battery" capability "Battery"
capability "Refresh" capability "Refresh"

View File

@@ -15,7 +15,7 @@
*/ */
metadata { metadata {
definition (name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
capability "Motion Sensor" capability "Motion Sensor"
capability "Configuration" capability "Configuration"
capability "Battery" capability "Battery"

View File

@@ -15,7 +15,7 @@
*/ */
metadata { metadata {
definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
capability "Three Axis" capability "Three Axis"
capability "Battery" capability "Battery"

View File

@@ -16,7 +16,7 @@
//DEPRECATED - Using the smartsense-multi-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH //DEPRECATED - Using the smartsense-multi-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
metadata { metadata {
definition (name: "SmartSense Open/Closed Accelerometer Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Open/Closed Accelerometer Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") {
capability "Battery" capability "Battery"
capability "Configuration" capability "Configuration"
capability "Contact Sensor" capability "Contact Sensor"

View File

@@ -14,7 +14,7 @@
* *
*/ */
metadata { metadata {
definition (name: "SmartSense Temp/Humidity Sensor",namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Temp/Humidity Sensor",namespace: "smartthings", author: "SmartThings", category: "C2") {
capability "Configuration" capability "Configuration"
capability "Battery" capability "Battery"
capability "Refresh" capability "Refresh"

View File

@@ -22,7 +22,7 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
valueTile("currentColor", "device.color") { valueTile("currentColor", "device.color") {
state "default", label: '${currentValue}' state "color", label: '${currentValue}', defaultState: true
} }
controlTile("rgbSelector", "device.color", "color", height: 6, width: 6, inactiveLabel: false) { controlTile("rgbSelector", "device.color", "color", height: 6, width: 6, inactiveLabel: false) {
@@ -41,6 +41,13 @@ def parse(String description) {
log.debug "Parsing '${description}'" log.debug "Parsing '${description}'"
} }
def setColor(value) {
log.debug "setting color: $value"
if (value.hex) { sendEvent(name: "color", value: value.hex) }
if (value.hue) { sendEvent(name: "hue", value: value.hue) }
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation) }
}
def setSaturation(percent) { def setSaturation(percent) {
log.debug "Executing 'setSaturation'" log.debug "Executing 'setSaturation'"
sendEvent(name: "saturation", value: percent) sendEvent(name: "saturation", value: percent)

View File

@@ -39,7 +39,7 @@ metadata {
} }
valueTile("rangeValue", "device.rangedLevel", height: 2, width: 2) { valueTile("rangeValue", "device.rangedLevel", height: 2, width: 2) {
state "default", label:'${currentValue}' state "range", label:'${currentValue}', defaultState: true
} }
controlTile("rangeSliderConstrained", "device.rangedLevel", "slider", height: 2, width: 4, range: "(40..60)") { controlTile("rangeSliderConstrained", "device.rangedLevel", "slider", height: 2, width: 4, range: "(40..60)") {

View File

@@ -41,17 +41,17 @@ metadata {
// standard flat tile with only a label // standard flat tile with only a label
standardTile("flatLabel", "device.switch", width: 2, height: 2, decoration: "flat") { standardTile("flatLabel", "device.switch", width: 2, height: 2, decoration: "flat") {
state "default", label: 'On Action', action: "switch.on", backgroundColor: "#ffffff" state "label", label: 'On Action', action: "switch.on", backgroundColor: "#ffffff", defaultState: true
} }
// standard flat tile with icon and label // standard flat tile with icon and label
standardTile("flatIconLabel", "device.switch", width: 2, height: 2, decoration: "flat") { standardTile("flatIconLabel", "device.switch", width: 2, height: 2, decoration: "flat") {
state "default", label: 'Off Action', action: "switch.off", icon:"st.switches.switch.off", backgroundColor: "#ffffff" state "iconLabel", label: 'Off Action', action: "switch.off", icon:"st.switches.switch.off", backgroundColor: "#ffffff", defaultState: true
} }
// standard flat tile with only icon (Refreh text is IN the icon file) // standard flat tile with only icon (Refreh text is IN the icon file)
standardTile("flatIcon", "device.switch", width: 2, height: 2, decoration: "flat") { standardTile("flatIcon", "device.switch", width: 2, height: 2, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh" state "icon", action:"refresh.refresh", icon:"st.secondary.refresh", defaultState: true
} }
// standard with defaultState = true // standard with defaultState = true
@@ -74,19 +74,19 @@ metadata {
// utility tiles to fill the spaces // utility tiles to fill the spaces
standardTile("empty2x2", "null", width: 2, height: 2, decoration: "flat") { standardTile("empty2x2", "null", width: 2, height: 2, decoration: "flat") {
state "default", label:'' state "emptySmall", label:'', defaultState: true
} }
standardTile("empty4x2", "null", width: 4, height: 2, decoration: "flat") { standardTile("empty4x2", "null", width: 4, height: 2, decoration: "flat") {
state "default", label:'' state "emptyBigger", label:'', defaultState: true
} }
// multi-line text (explicit newlines) // multi-line text (explicit newlines)
standardTile("multiLine", "device.multiLine", width: 2, height: 2) { standardTile("multiLine", "device.multiLine", width: 2, height: 2) {
state "default", label: '${currentValue}' state "multiLine", label: '${currentValue}', defaultState: true
} }
standardTile("multiLineWithIcon", "device.multiLine", width: 2, height: 2) { standardTile("multiLineWithIcon", "device.multiLine", width: 2, height: 2) {
state "default", label: '${currentValue}', icon: "st.switches.switch.off" state "multiLineIcon", label: '${currentValue}', icon: "st.switches.switch.off", defaultState: true
} }
main("actionRings") main("actionRings")

View File

@@ -22,63 +22,63 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
valueTile("text", "device.text", width: 2, height: 2) { valueTile("text", "device.text", width: 2, height: 2) {
state "default", label:'${currentValue}' state "val", label:'${currentValue}', defaultState: true
} }
valueTile("longText", "device.longText", width: 2, height: 2) { valueTile("longText", "device.longText", width: 2, height: 2) {
state "default", label:'${currentValue}' state "val", label:'${currentValue}', defaultState: true
} }
valueTile("integer", "device.integer", width: 2, height: 2) { valueTile("integer", "device.integer", width: 2, height: 2) {
state "default", label:'${currentValue}' state "val", label:'${currentValue}', defaultState: true
} }
valueTile("integerFloat", "device.integerFloat", width: 2, height: 2) { valueTile("integerFloat", "device.integerFloat", width: 2, height: 2) {
state "default", label:'${currentValue}' state "val", label:'${currentValue}', defaultState: true
} }
valueTile("pi", "device.pi", width: 2, height: 2) { valueTile("pi", "device.pi", width: 2, height: 2) {
state "default", label:'${currentValue}' state "val", label:'${currentValue}', defaultState: true
} }
valueTile("floatAsText", "device.floatAsText", width: 2, height: 2) { valueTile("floatAsText", "device.floatAsText", width: 2, height: 2) {
state "default", label:'${currentValue}' state "val", label:'${currentValue}', defaultState: true
} }
valueTile("bgColor", "device.integer", width: 2, height: 2) { valueTile("bgColor", "device.integer", width: 2, height: 2) {
state "default", label:'${currentValue}', backgroundColor: "#e86d13" state "val", label:'${currentValue}', backgroundColor: "#e86d13", defaultState: true
} }
valueTile("bgColorRange", "device.integer", width: 2, height: 2) { valueTile("bgColorRange", "device.integer", width: 2, height: 2) {
state "default", label:'${currentValue}', backgroundColors: [ state "val", label:'${currentValue}', defaultState: true, backgroundColors: [
[value: 10, color: "#ff0000"], [value: 10, color: "#ff0000"],
[value: 90, color: "#0000ff"] [value: 90, color: "#0000ff"]
] ]
} }
valueTile("bgColorRangeSingleItem", "device.integer", width: 2, height: 2) { valueTile("bgColorRangeSingleItem", "device.integer", width: 2, height: 2) {
state "default", label:'${currentValue}', backgroundColors: [ state "val", label:'${currentValue}', defaultState: true, backgroundColors: [
[value: 10, color: "#333333"] [value: 10, color: "#333333"]
] ]
} }
valueTile("bgColorRangeConflict", "device.integer", width: 2, height: 2) { valueTile("bgColorRangeConflict", "device.integer", width: 2, height: 2) {
state "default", label:'${currentValue}', backgroundColors: [ state "valWithConflict", label:'${currentValue}', defaultState: true, backgroundColors: [
[value: 10, color: "#990000"], [value: 10, color: "#990000"],
[value: 10, color: "#000099"] [value: 10, color: "#000099"]
] ]
} }
valueTile("noValue", "device.nada", width: 4, height: 2) { valueTile("noValue", "device.nada", width: 4, height: 2) {
state "default", label:'${currentValue}' state "noval", label:'${currentValue}', defaultState: true
} }
valueTile("multiLine", "device.multiLine", width: 3, height: 2) { valueTile("multiLine", "device.multiLine", width: 3, height: 2) {
state "default", label: '${currentValue}' state "val", label: '${currentValue}', defaultState: true
} }
valueTile("multiLineWithIcon", "device.multiLine", width: 3, height: 2) { valueTile("multiLineWithIcon", "device.multiLine", width: 3, height: 2) {
state "default", label: '${currentValue}', icon: "st.switches.switch.off" state "val", label: '${currentValue}', icon: "st.switches.switch.off", defaultState: true
} }
main("text") main("text")

View File

@@ -39,15 +39,15 @@ metadata {
attributeState "turningOff", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute("device.level", key: "SECONDARY_CONTROL") { tileAttribute("device.level", key: "SECONDARY_CONTROL") {
attributeState "default", icon: 'st.Weather.weather1', action:"randomizeLevel" attributeState "level", icon: 'st.Weather.weather1', action:"randomizeLevel", defaultState: true
} }
tileAttribute("device.level", key: "SLIDER_CONTROL") { tileAttribute("device.level", key: "SLIDER_CONTROL") {
attributeState "default", action:"switch level.setLevel" attributeState "level", action:"switch level.setLevel", defaultState: true
} }
} }
multiAttributeTile(name:"valueTile", type:"generic", width:6, height:4) { multiAttributeTile(name:"valueTile", type:"generic", width:6, height:4) {
tileAttribute("device.level", key: "PRIMARY_CONTROL") { tileAttribute("device.level", key: "PRIMARY_CONTROL") {
attributeState "default", label:'${currentValue}', backgroundColors:[ attributeState "level", label:'${currentValue}', defaultState: true, backgroundColors:[
[value: 0, color: "#ff0000"], [value: 0, color: "#ff0000"],
[value: 20, color: "#ffff00"], [value: 20, color: "#ffff00"],
[value: 40, color: "#00ff00"], [value: 40, color: "#00ff00"],
@@ -69,34 +69,34 @@ metadata {
} }
multiAttributeTile(name:"lengthyTile", type:"generic", width:6, height:4) { multiAttributeTile(name:"lengthyTile", type:"generic", width:6, height:4) {
tileAttribute("device.lengthyText", key: "PRIMARY_CONTROL") { tileAttribute("device.lengthyText", key: "PRIMARY_CONTROL") {
attributeState "default", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821" attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", defaultState: true
} }
tileAttribute("device.lengthyText", key: "SECONDARY_CONTROL") { tileAttribute("device.lengthyText", key: "SECONDARY_CONTROL") {
attributeState "default", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821" attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", defaultState: true
} }
} }
multiAttributeTile(name:"multilineTile", type:"generic", width:6, height:4) { multiAttributeTile(name:"multilineTile", type:"generic", width:6, height:4) {
tileAttribute("device.multilineText", key: "PRIMARY_CONTROL") { tileAttribute("device.multilineText", key: "PRIMARY_CONTROL") {
attributeState "default", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821" attributeState "multiLineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", defaultState: true
} }
tileAttribute("device.multilineText", key: "SECONDARY_CONTROL") { tileAttribute("device.multilineText", key: "SECONDARY_CONTROL") {
attributeState "default", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821" attributeState "multiLineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", defaultState: true
} }
} }
multiAttributeTile(name:"lengthyTileWithIcon", type:"generic", width:6, height:4) { multiAttributeTile(name:"lengthyTileWithIcon", type:"generic", width:6, height:4) {
tileAttribute("device.lengthyText", key: "PRIMARY_CONTROL") { tileAttribute("device.lengthyText", key: "PRIMARY_CONTROL") {
attributeState "default", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", icon: "st.switches.switch.on" attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
} }
tileAttribute("device.lengthyText", key: "SECONDARY_CONTROL") { tileAttribute("device.lengthyText", key: "SECONDARY_CONTROL") {
attributeState "default", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", icon: "st.switches.switch.on" attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
} }
} }
multiAttributeTile(name:"multilineTileWithIcon", type:"generic", width:6, height:4) { multiAttributeTile(name:"multilineTileWithIcon", type:"generic", width:6, height:4) {
tileAttribute("device.multilineText", key: "PRIMARY_CONTROL") { tileAttribute("device.multilineText", key: "PRIMARY_CONTROL") {
attributeState "default", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", icon: "st.switches.switch.on" attributeState "multilineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
} }
tileAttribute("device.multilineText", key: "SECONDARY_CONTROL") { tileAttribute("device.multilineText", key: "SECONDARY_CONTROL") {
attributeState "default", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", icon: "st.switches.switch.on" attributeState "multilineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
} }
} }

View File

@@ -96,10 +96,10 @@ metadata {
} }
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single" state "reset", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single", defaultState: true
} }
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" state "refresh", label:"", action:"refresh.refresh", icon:"st.secondary.refresh", defaultState: true
} }
main(["switch"]) main(["switch"])
@@ -173,7 +173,6 @@ def setColor(value) {
def reset() { def reset() {
log.debug "Executing 'reset'" log.debug "Executing 'reset'"
setAdjustedColor([level:100, hex:"#90C638", saturation:56, hue:23]) setAdjustedColor([level:100, hex:"#90C638", saturation:56, hue:23])
//parent.poll()
} }
def setAdjustedColor(value) { def setAdjustedColor(value) {
@@ -189,7 +188,6 @@ def setAdjustedColor(value) {
def refresh() { def refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
//parent.manualRefresh()
} }
def adjustOutgoingHue(percent) { def adjustOutgoingHue(percent) {
@@ -208,4 +206,3 @@ def adjustOutgoingHue(percent) {
log.info "percent: $percent, adjusted: $adjusted" log.info "percent: $percent, adjusted: $adjusted"
adjusted adjusted
} }

View File

@@ -37,10 +37,10 @@ metadata {
attributeState("stopped", label:"Stopped", action:"music Player.play", nextState: "playing") attributeState("stopped", label:"Stopped", action:"music Player.play", nextState: "playing")
} }
tileAttribute("device.status", key: "PREVIOUS_TRACK") { tileAttribute("device.status", key: "PREVIOUS_TRACK") {
attributeState("default", action:"music Player.previousTrack") attributeState("status", action:"music Player.previousTrack", defaultState: true)
} }
tileAttribute("device.status", key: "NEXT_TRACK") { tileAttribute("device.status", key: "NEXT_TRACK") {
attributeState("default", action:"music Player.nextTrack") attributeState("status", action:"music Player.nextTrack", defaultState: true)
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState("level", action:"music Player.setLevel") attributeState("level", action:"music Player.setLevel")
@@ -50,7 +50,7 @@ metadata {
attributeState("muted", action:"music Player.unmute", nextState: "unmuted") attributeState("muted", action:"music Player.unmute", nextState: "unmuted")
} }
tileAttribute("device.trackDescription", key: "MARQUEE") { tileAttribute("device.trackDescription", key: "MARQUEE") {
attributeState("default", label:"${currentValue}") attributeState("trackDescription", label:"${currentValue}", defaultState: true)
} }
} }

View File

@@ -32,14 +32,14 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"thermostatFull", type:"thermostat", width:6, height:4) { multiAttributeTile(name:"thermostatFull", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") { tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("default", label:'${currentValue}', unit:"dF") attributeState("temp", label:'${currentValue}', unit:"dF", defaultState: true)
} }
tileAttribute("device.temperature", key: "VALUE_CONTROL") { tileAttribute("device.temperature", key: "VALUE_CONTROL") {
attributeState("VALUE_UP", action: "tempUp") attributeState("VALUE_UP", action: "tempUp")
attributeState("VALUE_DOWN", action: "tempDown") attributeState("VALUE_DOWN", action: "tempDown")
} }
tileAttribute("device.humidity", key: "SECONDARY_CONTROL") { tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
attributeState("default", label:'${currentValue}%', unit:"%") attributeState("humidity", label:'${currentValue}%', unit:"%", defaultState: true)
} }
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") { tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
attributeState("idle", backgroundColor:"#44b621") attributeState("idle", backgroundColor:"#44b621")
@@ -53,15 +53,16 @@ metadata {
attributeState("auto", label:'${name}') attributeState("auto", label:'${name}')
} }
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") { tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
attributeState("default", label:'${currentValue}', unit:"dF") attributeState("heatingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
} }
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") { tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
attributeState("default", label:'${currentValue}', unit:"dF") attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
} }
} }
multiAttributeTile(name:"thermostatNoHumidity", type:"thermostat", width:6, height:4) { multiAttributeTile(name:"thermostatNoHumidity", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") { tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("default", label:'${currentValue}', unit:"dF") attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
attributeState("temp", label:'${currentValue}', unit:"dF")
} }
tileAttribute("device.temperature", key: "VALUE_CONTROL") { tileAttribute("device.temperature", key: "VALUE_CONTROL") {
attributeState("VALUE_UP", action: "tempUp") attributeState("VALUE_UP", action: "tempUp")
@@ -79,15 +80,16 @@ metadata {
attributeState("auto", label:'${name}') attributeState("auto", label:'${name}')
} }
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") { tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
attributeState("default", label:'${currentValue}', unit:"dF") attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
attributeState("heatingSetpoint", label:'${currentValue}', unit:"dF")
} }
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") { tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
attributeState("default", label:'${currentValue}', unit:"dF") attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
} }
} }
multiAttributeTile(name:"thermostatBasic", type:"thermostat", width:6, height:4) { multiAttributeTile(name:"thermostatBasic", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") { tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("default", label:'${currentValue}', unit:"dF", attributeState("temp", label:'${currentValue}', unit:"dF", defaultState: true,
backgroundColors:[ backgroundColors:[
[value: 31, color: "#153591"], [value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"], [value: 44, color: "#1e9cbb"],
@@ -118,30 +120,30 @@ metadata {
) )
} }
standardTile("tempDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { standardTile("tempDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'down', action:"tempDown" state "tempDown", label:'down', action:"tempDown", defaultState: true
} }
standardTile("tempUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { standardTile("tempUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'up', action:"tempUp" state "tempUp", label:'up', action:"tempUp", defaultState: true
} }
valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "heat", label:'${currentValue} heat', unit: "F", backgroundColor:"#ffffff" state "heat", label:'${currentValue} heat', unit: "F", backgroundColor:"#ffffff"
} }
standardTile("heatDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { standardTile("heatDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'down', action:"heatDown" state "heatDown", label:'down', action:"heatDown", defaultState: true
} }
standardTile("heatUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { standardTile("heatUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'up', action:"heatUp" state "heatUp", label:'up', action:"heatUp", defaultState: true
} }
valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "cool", label:'${currentValue} cool', unit:"F", backgroundColor:"#ffffff" state "cool", label:'${currentValue} cool', unit:"F", backgroundColor:"#ffffff"
} }
standardTile("coolDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { standardTile("coolDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'down', action:"coolDown" state "coolDown", label:'down', action:"coolDown", defaultState: true
} }
standardTile("coolUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { standardTile("coolUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'up', action:"coolUp" state "coolUp", label:'up', action:"coolUp", defaultState: true
} }
standardTile("mode", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { standardTile("mode", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {

View File

@@ -95,11 +95,17 @@ def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
} }
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
if (state.manufacturer != cmd.manufacturerName) { log.debug "manufacturerId: ${cmd.manufacturerId}"
updateDataValue("manufacturer", cmd.manufacturerName) log.debug "manufacturerName: ${cmd.manufacturerName}"
} log.debug "productId: ${cmd.productId}"
log.debug "productTypeId: ${cmd.productTypeId}"
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
updateDataValue("MSR", msr)
updateDataValue("manufacturer", cmd.manufacturerName)
createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
} }
def zwaveEvent(physicalgraph.zwave.Command cmd) { def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we aren't interested in // Handles all Z-Wave commands we aren't interested in
[:] [:]

View File

@@ -4,29 +4,33 @@
import java.text.DecimalFormat import java.text.DecimalFormat
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
private apiUrl() { "https://api.netatmo.com" } private getApiUrl() { "https://api.netatmo.com" }
private getVendorName() { "netatmo" } private getVendorName() { "netatmo" }
private getVendorAuthPath() { "https://api.netatmo.com/oauth2/authorize?" } private getVendorAuthPath() { "${apiUrl}/oauth2/authorize?" }
private getVendorTokenPath(){ "https://api.netatmo.com/oauth2/token" } private getVendorTokenPath(){ "${apiUrl}/oauth2/token" }
private getVendorIcon() { "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1%402x.png" } private getVendorIcon() { "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1%402x.png" }
private getClientId() { appSettings.clientId } private getClientId() { appSettings.clientId }
private getClientSecret() { appSettings.clientSecret } private getClientSecret() { appSettings.clientSecret }
private getServerUrl() { "https://graph.api.smartthings.com" } private getServerUrl() { appSettings.serverUrl }
private getShardUrl() { return getApiServerUrl() }
private getCallbackUrl() { "${serverUrl}/oauth/callback" }
private getBuildRedirectUrl() { "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${shardUrl}" }
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition( definition(
name: "Netatmo (Connect)", name: "Netatmo (Connect)",
namespace: "dianoga", namespace: "dianoga",
author: "Brian Steere", author: "Brian Steere",
description: "Netatmo Integration", description: "Netatmo Integration",
category: "SmartThings Labs", category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1.png", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1%402x.png", iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/netamo-icon-1%402x.png",
oauth: true, oauth: true,
singleInstance: true singleInstance: true
){ ){
appSetting "clientId" appSetting "clientId"
appSetting "clientSecret" appSetting "clientSecret"
appSetting "serverUrl"
} }
preferences { preferences {
@@ -35,35 +39,52 @@ preferences {
} }
mappings { mappings {
path("/receivedToken"){action: [POST: "receivedToken", GET: "receivedToken"]} path("/oauth/initialize") {action: [GET: "oauthInitUrl"]}
path("/receiveToken"){action: [POST: "receiveToken", GET: "receiveToken"]} path("/oauth/callback") {action: [GET: "callback"]}
path("/auth"){action: [GET: "auth"]}
} }
def authPage() { def authPage() {
log.debug "In authPage" log.debug "In authPage"
if(canInstallLabs()) {
def description = null
if (state.vendorAccessToken == null) { def description
log.debug "About to create access token." def uninstallAllowed = false
def oauthTokenProvided = false
createAccessToken() if (!state.accessToken) {
description = "Tap to enter Credentials." log.debug "About to create access token."
state.accessToken = createAccessToken()
}
return dynamicPage(name: "Credentials", title: "Authorize Connection", nextPage:"listDevices", uninstall: true, install:false) { if (canInstallLabs()) {
section { href url:buildRedirectUrl("auth"), style:"embedded", required:false, title:"Connect to ${getVendorName()}:", description:description }
def redirectUrl = getBuildRedirectUrl()
log.debug "Redirect url = ${redirectUrl}"
if (state.authToken) {
description = "Tap 'Next' to proceed"
uninstallAllowed = true
oauthTokenProvided = true
} else {
description = "Click to enter Credentials."
}
if (!oauthTokenProvided) {
log.debug "Show the login page"
return dynamicPage(name: "Credentials", title: "Authorize Connection", nextPage:"listDevices", uninstall: uninstallAllowed, install:false) {
section() {
paragraph "Tap below to log in to the netatmo and authorize SmartThings access."
href url:redirectUrl, style:"embedded", required:false, title:"Connect to ${getVendorName()}:", description:description
}
} }
} else { } else {
description = "Tap 'Next' to proceed" log.debug "Show the devices page"
return dynamicPage(name: "Credentials", title: "Credentials Accepted!", nextPage:"listDevices", uninstall: uninstallAllowed, install:false) {
return dynamicPage(name: "Credentials", title: "Credentials Accepted!", nextPage:"listDevices", uninstall: true, install:false) { section() {
section { href url: buildRedirectUrl("receivedToken"), style:"embedded", required:false, title:"${getVendorName()} is now connected to SmartThings!", description:description } input(name:"Devices", style:"embedded", required:false, title:"${getVendorName()} is now connected to SmartThings!", description:description)
}
} }
} }
} } else {
else
{
def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date. def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date.
To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub".""" To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub"."""
@@ -78,229 +99,175 @@ To update your Hub, access Location Settings in the Main Menu (tap the gear next
} }
} }
def auth() {
redirect location: oauthInitUrl()
}
def oauthInitUrl() { def oauthInitUrl() {
log.debug "In oauthInitUrl" log.debug "In oauthInitUrl"
/* OAuth Step 1: Request access code with our client ID */
state.oauthInitState = UUID.randomUUID().toString() state.oauthInitState = UUID.randomUUID().toString()
def oauthParams = [ response_type: "code",
client_id: getClientId(),
state: state.oauthInitState,
redirect_uri: buildRedirectUrl("receiveToken") ,
scope: "read_station"
]
return getVendorAuthPath() + toQueryString(oauthParams)
}
def buildRedirectUrl(endPoint) {
log.debug "In buildRedirectUrl"
return getServerUrl() + "/api/token/${state.accessToken}/smartapps/installations/${app.id}/${endPoint}"
}
def receiveToken() {
log.debug "In receiveToken"
def oauthParams = [ def oauthParams = [
client_secret: getClientSecret(), response_type: "code",
client_id: getClientId(), client_id: getClientId(),
grant_type: "authorization_code", client_secret: getClientSecret(),
redirect_uri: buildRedirectUrl('receiveToken'), state: state.oauthInitState,
code: params.code, redirect_uri: getCallbackUrl(),
scope: "read_station" scope: "read_station"
]
def tokenUrl = getVendorTokenPath()
def params = [
uri: tokenUrl,
contentType: 'application/x-www-form-urlencoded',
body: oauthParams,
] ]
log.debug params log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
/* OAuth Step 2: Request access token with our client Secret and OAuth "Code" */ redirect (location: getVendorAuthPath() + toQueryString(oauthParams))
try { }
httpPost(params) { response ->
log.debug response.data
def slurper = new JsonSlurper();
response.data.each {key, value -> def callback() {
def data = slurper.parseText(key); log.debug "callback()>> params: $params, params.code ${params.code}"
log.debug "Data: $data"
state.vendorRefreshToken = data.refresh_token def code = params.code
state.vendorAccessToken = data.access_token def oauthState = params.state
state.vendorTokenExpires = now() + (data.expires_in * 1000)
return if (oauthState == state.oauthInitState) {
def tokenParams = [
client_secret: getClientSecret(),
client_id : getClientId(),
grant_type: "authorization_code",
redirect_uri: getCallbackUrl(),
code: code,
scope: "read_station"
]
log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
def tokenUrl = getVendorTokenPath()
def params = [
uri: tokenUrl,
contentType: 'application/x-www-form-urlencoded',
body: tokenParams
]
log.debug "PARAMS: ${params}"
httpPost(params) { resp ->
def slurper = new JsonSlurper()
resp.data.each { key, value ->
def data = slurper.parseText(key)
state.refreshToken = data.refresh_token
state.authToken = data.access_token
state.tokenExpires = now() + (data.expires_in * 1000)
log.debug "swapped token: $resp.data"
} }
} }
} catch (Exception e) {
log.debug "Error: $e" // Handle success and failure here, and render stuff accordingly
if (state.authToken) {
success()
} else {
fail()
}
} else {
log.error "callback() failed oauthState != state.oauthInitState"
} }
}
log.debug "State: $state" def success() {
log.debug "in success"
def message = """
<p>We have located your """ + getVendorName() + """ account.</p>
<p>Tap 'Done' to continue to Devices.</p>
"""
connectionStatus(message)
}
if ( !state.vendorAccessToken ) { //We didn't get an access token, bail on install def fail() {
return log.debug "in fail"
def message = """
<p>The connection could not be established!</p>
<p>Click 'Done' to return to the menu.</p>
"""
connectionStatus(message)
}
def connectionStatus(message, redirectUrl = null) {
def redirectHtml = ""
if (redirectUrl) {
redirectHtml = """
<meta http-equiv="refresh" content="3; url=${redirectUrl}" />
"""
} }
/* OAuth Step 3: Use the access token to call into the vendor API throughout your code using state.vendorAccessToken. */
def html = """ def html = """
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>${getVendorName()} Connection</title> <title>${getVendorName()} Connection</title>
<style type="text/css"> <style type="text/css">
* { box-sizing: border-box; } * { box-sizing: border-box; }
@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: 100%; width: 100%;
padding: 40px; padding: 40px;
/*background: #eee;*/ /*background: #eee;*/
text-align: center; text-align: center;
} }
img { img {
vertical-align: middle; vertical-align: middle;
} }
img:nth-child(2) { img:nth-child(2) {
margin: 0 30px; margin: 0 30px;
} }
p { p {
font-size: 2.2em; font-size: 2.2em;
font-family: 'Swiss 721 W01 Thin'; font-family: 'Swiss 721 W01 Thin';
text-align: center; text-align: center;
color: #666666; color: #666666;
margin-bottom: 0; margin-bottom: 0;
} }
/* /*
p:last-child { p:last-child {
margin-top: 0px; margin-top: 0px;
} }
*/ */
span { span {
font-family: 'Swiss 721 W01 Light'; font-family: 'Swiss 721 W01 Light';
} }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<img src=""" + getVendorIcon() + """ alt="Vendor icon" /> <img src=""" + getVendorIcon() + """ alt="Vendor icon" />
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png" alt="connected device icon" /> <img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png" alt="connected device icon" />
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png" alt="SmartThings logo" /> <img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png" alt="SmartThings logo" />
<p>We have located your """ + getVendorName() + """ account.</p> ${message}
<p>Tap 'Done' to process your credentials.</p>
</div> </div>
</body> </body>
</html> </html>
""" """
render contentType: 'text/html', data: html render contentType: 'text/html', data: html
} }
def receivedToken() {
log.debug "In receivedToken"
def html = """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Withings Connection</title>
<style type="text/css">
* { box-sizing: border-box; }
@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: 560px;
padding: 40px;
/*background: #eee;*/
text-align: center;
}
img {
vertical-align: middle;
}
img:nth-child(2) {
margin: 0 30px;
}
p {
font-size: 2.2em;
font-family: 'Swiss 721 W01 Thin';
text-align: center;
color: #666666;
margin-bottom: 0;
}
/*
p:last-child {
margin-top: 0px;
}
*/
span {
font-family: 'Swiss 721 W01 Light';
}
</style>
</head>
<body>
<div class="container">
<img src=""" + getVendorIcon() + """ alt="Vendor icon" />
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png" alt="connected device icon" />
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png" alt="SmartThings logo" />
<p>Tap 'Done' to continue to Devices.</p>
</div>
</body>
</html>
"""
render contentType: 'text/html', data: html
}
// "
def refreshToken() { def refreshToken() {
log.debug "In refreshToken" log.debug "In refreshToken"
@@ -308,8 +275,8 @@ def refreshToken() {
client_secret: getClientSecret(), client_secret: getClientSecret(),
client_id: getClientId(), client_id: getClientId(),
grant_type: "refresh_token", grant_type: "refresh_token",
refresh_token: state.vendorRefreshToken refresh_token: state.refreshToken
] ]
def tokenUrl = getVendorTokenPath() def tokenUrl = getVendorTokenPath()
def params = [ def params = [
@@ -318,7 +285,7 @@ def refreshToken() {
body: oauthParams, body: oauthParams,
] ]
/* OAuth Step 2: Request access token with our client Secret and OAuth "Code" */ // OAuth Step 2: Request access token with our client Secret and OAuth "Code"
try { try {
httpPost(params) { response -> httpPost(params) { response ->
def slurper = new JsonSlurper(); def slurper = new JsonSlurper();
@@ -327,9 +294,9 @@ def refreshToken() {
def data = slurper.parseText(key); def data = slurper.parseText(key);
log.debug "Data: $data" log.debug "Data: $data"
state.vendorRefreshToken = data.refresh_token state.refreshToken = data.refresh_token
state.vendorAccessToken = data.access_token state.accessToken = data.access_token
state.vendorTokenExpires = now() + (data.expires_in * 1000) state.tokenExpires = now() + (data.expires_in * 1000)
return true return true
} }
@@ -338,9 +305,8 @@ def refreshToken() {
log.debug "Error: $e" log.debug "Error: $e"
} }
log.debug "State: $state" // We didn't get an access token
if ( !state.accessToken ) {
if ( !state.vendorAccessToken ) { //We didn't get an access token
return false return false
} }
} }
@@ -482,13 +448,13 @@ def listDevices() {
} }
def apiGet(String path, Map query, Closure callback) { def apiGet(String path, Map query, Closure callback) {
if(now() >= state.vendorTokenExpires) { if(now() >= state.tokenExpires) {
refreshToken(); refreshToken();
} }
query['access_token'] = state.vendorAccessToken query['access_token'] = state.accessToken
def params = [ def params = [
uri: apiUrl(), uri: getApiUrl(),
path: path, path: path,
'query': query 'query': query
] ]

View File

@@ -370,9 +370,7 @@ def parse_api_response(resp, message) {
} }
} }
def getServerUrl() { def getServerUrl() { return getApiServerUrl() }
return "https://graph.api.smartthings.com"
}
def debugEvent(message, displayEvent) { def debugEvent(message, displayEvent) {
def results = [ def results = [

View File

@@ -297,8 +297,8 @@ private getTimeOk() {
def result = true def result = true
if (starting && ending) { if (starting && ending) {
def currTime = now() def currTime = now()
def start = timeToday(starting).time def start = timeToday(starting, location.timeZone).time
def stop = timeToday(ending).time def stop = timeToday(ending, location.timeZone).time
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
} }
log.trace "timeOk = $result" log.trace "timeOk = $result"

View File

@@ -27,7 +27,6 @@ definition(
) { ) {
appSetting "clientId" appSetting "clientId"
appSetting "clientSecret" appSetting "clientSecret"
appSetting "serverUrl"
} }
preferences { preferences {
@@ -192,7 +191,7 @@ def getSmartThingsClientId() {
return "pREqugabRetre4EstetherufrePumamExucrEHuc" return "pREqugabRetre4EstetherufrePumamExucrEHuc"
} }
def getServerUrl() { appSettings.serverUrl } def getServerUrl() { getApiServerUrl() }
def buildRedirectUrl() def buildRedirectUrl()
{ {