mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-18 21:03:39 +00:00
Compare commits
22 Commits
PROD_2016.
...
MSA-1282-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eaa5575ad2 | ||
|
|
feba6643a6 | ||
|
|
49c893771d | ||
|
|
d73f4c2ded | ||
|
|
13a056ec8d | ||
|
|
f55452d1c6 | ||
|
|
14b5fd41b8 | ||
|
|
0f4d7bd520 | ||
|
|
a95fbf2612 | ||
|
|
0f60ca61cb | ||
|
|
293a73136e | ||
|
|
8de3276ce6 | ||
|
|
f77c5810c6 | ||
|
|
9fc5f14dd7 | ||
|
|
55d7a4a263 | ||
|
|
b33d621696 | ||
|
|
45b78eff8d | ||
|
|
a133406b6e | ||
|
|
bd62962ee1 | ||
|
|
a5da182bf4 | ||
|
|
27c05f4e5b | ||
|
|
91c01dc643 |
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)") {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)") {
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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") {
|
||||||
|
|||||||
@@ -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
|
||||||
[:]
|
[:]
|
||||||
|
|||||||
@@ -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
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user