mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-19 13:20:53 +00:00
Compare commits
1 Commits
PROD_2016.
...
MSA-1463-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf2c2672c8 |
30
build.gradle
30
build.gradle
@@ -19,7 +19,7 @@ buildscript {
|
|||||||
username smartThingsArtifactoryUserName
|
username smartThingsArtifactoryUserName
|
||||||
password smartThingsArtifactoryPassword
|
password smartThingsArtifactoryPassword
|
||||||
}
|
}
|
||||||
url "https://artifactory.smartthings.com/libs-release-local"
|
url "http://artifactory.smartthings.com/libs-release-local"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,37 +27,9 @@ buildscript {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
|
||||||
credentials {
|
|
||||||
username smartThingsArtifactoryUserName
|
|
||||||
password smartThingsArtifactoryPassword
|
|
||||||
}
|
|
||||||
url "https://artifactory.smartthings.com/libs-release-local"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
devicetypes {
|
|
||||||
groovy {
|
|
||||||
srcDirs = ['devicetypes']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
smartapps {
|
|
||||||
groovy {
|
|
||||||
srcDirs = ['smartapps']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
devicetypesCompile 'org.codehaus.groovy:groovy-all:2.4.7'
|
|
||||||
devicetypesCompile 'smartthings:appengine-z-wave:0.1.2'
|
|
||||||
devicetypesCompile 'smartthings:appengine-zigbee:0.1.11'
|
|
||||||
smartappsCompile 'org.codehaus.groovy:groovy-all:2.4.7'
|
|
||||||
smartappsCompile 'smartthings:appengine-common:0.1.8'
|
|
||||||
smartappsCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1'
|
|
||||||
smartappsCompile 'org.grails:grails-web:2.3.11'
|
|
||||||
smartappsCompile 'org.json:json:20140107'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
slackSendMessage {
|
slackSendMessage {
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ machine:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
override:
|
override:
|
||||||
- ./gradlew dependencies -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD"
|
- echo "Nothing to do."
|
||||||
post:
|
|
||||||
- ./gradlew compileSmartappsGroovy compileDevicetypesGroovy -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD"
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
metadata {
|
||||||
|
definition (name: "Rooflight Controller", namespace: "CR76", author: "Cameron.Reid@Glazingvision.co.uk") {
|
||||||
|
capability "Refresh"
|
||||||
|
capability "Polling"
|
||||||
|
capability "Switch"
|
||||||
|
capability "Switch level"
|
||||||
|
|
||||||
|
attribute "Rooflight", "string"
|
||||||
|
attribute "Weather", "string"
|
||||||
|
attribute "Thermostat", "string"
|
||||||
|
|
||||||
|
command "Stop"
|
||||||
|
|
||||||
|
fingerprint profileId: "0104", inClusters: "0000,0006,0008"
|
||||||
|
}
|
||||||
|
|
||||||
|
// simulator metadata
|
||||||
|
simulator {
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI tile definitions
|
||||||
|
tiles {
|
||||||
|
multiAttributeTile(name:"sliderTile", type: "generic", width:6, height:4) {
|
||||||
|
tileAttribute("device.Rooflight", key: "PRIMARY_CONTROL") {
|
||||||
|
attributeState "Open", label:'Open', action: "Switch.off", backgroundColor:"#79b821", nextState:"Closing"
|
||||||
|
attributeState "Closed", label:'Closed', action: "Switch.on", backgroundColor:"#ffffff", nextState:"Opening"
|
||||||
|
attributeState "Opening", label:'Opening', backgroundColor:"#79b821"
|
||||||
|
attributeState "Closing", label:'Closing', backgroundColor:"#ffffff"
|
||||||
|
}
|
||||||
|
tileAttribute ("device.Rooflight", key: "SECONDARY_CONTROL") {
|
||||||
|
attributeState "Open", label: 'Push to close.', nextState:"Closing"
|
||||||
|
attributeState "Closed", label: 'Push to open.', nextState:"Opening"
|
||||||
|
attributeState "Opening", label: 'Skylight opening.'
|
||||||
|
attributeState "Closing", label: 'Skylight closing.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
standardTile("Close", "device.Rooflight", inactiveLabel: false, decoration: "flat", width:2, height:2 ) {
|
||||||
|
state "default", label: 'Close', action:"Switch.off", icon:"st.thermostat.thermostat-left"
|
||||||
|
}
|
||||||
|
standardTile("Stop", "device.level", inactiveLabel: false, decoration: "flat", width:2, height:2 ) {
|
||||||
|
state "default", action:"Stop", icon:"st.sonos.stop-btn"
|
||||||
|
}
|
||||||
|
standardTile("Open", "device.Rooflight", inactiveLabel: false, decoration: "flat", width:2, height:2 ) {
|
||||||
|
state "default", label: 'open', action:"Switch.on", icon:"st.thermostat.thermostat-right"
|
||||||
|
}
|
||||||
|
standardTile("Rain", "device.Weather", width: 2, height: 2) {
|
||||||
|
state "No", backgroundColor: "#ffffff"
|
||||||
|
state "Dry", label: 'Dry', icon:"st.Weather.weather14", backgroundColor: "#ffffff"
|
||||||
|
state "Raining", label: 'Raining', icon:"st.Weather.weather10", backgroundColor: "#153591"
|
||||||
|
}
|
||||||
|
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width:2, height:2 ) {
|
||||||
|
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
|
}
|
||||||
|
|
||||||
|
main ("sliderTile")
|
||||||
|
details (["sliderTile","Close","Stop","Open","Rain","refresh"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse incoming device messages to generate events
|
||||||
|
def parse(String description) {
|
||||||
|
log.debug "Parse description $description"
|
||||||
|
def name = null
|
||||||
|
def value = null
|
||||||
|
|
||||||
|
if (description?.startsWith("catchall: 0104 0006 38")) {
|
||||||
|
log.debug "On/Off command received from EP 38 open / close command"
|
||||||
|
if (description?.endsWith("01 01 0000001000")) {
|
||||||
|
name = "Rooflight"
|
||||||
|
value = "Closed"
|
||||||
|
}
|
||||||
|
else if (description?.endsWith("01 01 0000001001")) {
|
||||||
|
name = "Rooflight"
|
||||||
|
value = "Open"
|
||||||
|
}
|
||||||
|
else if (description?.endsWith("01 01 0000001003")) {
|
||||||
|
name = "Rooflight"
|
||||||
|
value = "Opening"
|
||||||
|
}
|
||||||
|
else if (description?.endsWith("01 01 0000001004")) {
|
||||||
|
name = "Rooflight"
|
||||||
|
value = "Closing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (description?.startsWith("catchall: 0104 0006 39")) {
|
||||||
|
log.debug "On/Off command received from EP 39 is rain sensor connected"
|
||||||
|
if (description?.endsWith("01 01 0000001000")) {
|
||||||
|
name = "Weather"
|
||||||
|
value = "No"
|
||||||
|
state.tile = 1
|
||||||
|
}
|
||||||
|
else if (description?.endsWith("01 01 0000001001")) {
|
||||||
|
name = "Weather"
|
||||||
|
value = "Dry"
|
||||||
|
state.tile = 0
|
||||||
|
log.debug "${state.tile}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (description?.startsWith("catchall: 0104 0006 40")) {
|
||||||
|
log.debug "On/Off command received from EP 40 Thermostat"
|
||||||
|
if (description?.endsWith("01 01 0000001000")){
|
||||||
|
name = "Switch"
|
||||||
|
value = "on"
|
||||||
|
}
|
||||||
|
else if (description?.endsWith("01 01 0000001001")) {
|
||||||
|
name = "Switch"
|
||||||
|
value = "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (description?.startsWith("catchall: 0104 0006 41")) {
|
||||||
|
log.debug "On/Off command received from EP 41 rain sensor"
|
||||||
|
if (description?.endsWith("01 01 0000001000")) {
|
||||||
|
name = "Weather"
|
||||||
|
value = "Dry"
|
||||||
|
}
|
||||||
|
else if (description?.endsWith("01 01 0000001001")) {
|
||||||
|
name = "Weather"
|
||||||
|
value = "Raining"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(description?.startsWith('read attr -')) {
|
||||||
|
def descMap = parseDescriptionAsMap(description) //Get level value.
|
||||||
|
value = zigbee.convertHexToInt(descMap.value)
|
||||||
|
log.debug " level = $value"
|
||||||
|
name = "level"
|
||||||
|
}
|
||||||
|
def result = createEvent(name: name, value: value)
|
||||||
|
log.debug "Parse returned ${result?.descriptionText}"
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
def parseDescriptionAsMap(description) {
|
||||||
|
(description - "read attr - ").split(",").inject([:]) {
|
||||||
|
map, param ->
|
||||||
|
def nameAndValue = param.split(":")
|
||||||
|
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands to device
|
||||||
|
def on() {
|
||||||
|
log.debug "Rooflight Opening"
|
||||||
|
def cmd = []
|
||||||
|
cmd <<"st cmd 0x${device.deviceNetworkId} 0x38 0x0006 0x1 {}"
|
||||||
|
cmd << "delay 250"
|
||||||
|
cmd << zigbee.command(0x0008, 0x04, "64")
|
||||||
|
}
|
||||||
|
|
||||||
|
def off() {
|
||||||
|
log.debug "Rooflight Closing"
|
||||||
|
def cmd = []
|
||||||
|
cmd << "st cmd 0x${device.deviceNetworkId} 0x38 0x0006 0x0 {}"
|
||||||
|
cmd << "delay 250"
|
||||||
|
cmd << zigbee.command(0x0008, 0x04, "00")
|
||||||
|
}
|
||||||
|
|
||||||
|
def Stop() { //Send stop command in the level control cluster.
|
||||||
|
log.debug "send stop"
|
||||||
|
"st cmd 0x${device.deviceNetworkId} 0x38 0x0008 0x07 {}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def poll() {
|
||||||
|
log.debug "Poll is calling refresh"
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
def refresh() {
|
||||||
|
log.debug "sending refresh command"
|
||||||
|
log.debug "Tile State: ${state.tile}"
|
||||||
|
def cmd = []
|
||||||
|
if("${state.tile}" == "0") { //If rain sensor is connected refresh rain sensor and rooflight position.
|
||||||
|
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0006 0x0000" // Read on / off attribute at End point 0x38 Rooflight open / closed.
|
||||||
|
cmd << "delay 250"
|
||||||
|
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0008 0x0000" // Read Level position at End point 0x38 Rooflight Position.
|
||||||
|
cmd << "delay 250"
|
||||||
|
cmd << "st rattr 0x${device.deviceNetworkId} 0x41 0x0006 0x0000" // Read on / off attribute at End point 0x41 Rain sensor.
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0006 0x0000" // Read on / off attribute at End point 0x38 Rooflight open / closed.
|
||||||
|
cmd << "delay 250"
|
||||||
|
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0008 0x0000" // Read on / off attribute at End point 0x88 Rain sensor.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") {
|
||||||
capability "Sensor"
|
|
||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") {
|
||||||
capability "Sensor"
|
|
||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") {
|
||||||
capability "Sensor"
|
|
||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") {
|
||||||
capability "Sensor"
|
|
||||||
|
|
||||||
attribute "rain", "number"
|
attribute "rain", "number"
|
||||||
attribute "rainSumHour", "number"
|
attribute "rainSumHour", "number"
|
||||||
attribute "rainSumDay", "number"
|
attribute "rainSumDay", "number"
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ metadata {
|
|||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||||
tileAttribute("device.motion", key:"PRIMARY_CONTROL") {
|
tileAttribute("device.motion", key:"PRIMARY_CONTROL") {
|
||||||
attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
attributeState("inactive", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
||||||
attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
attributeState("active", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
||||||
}
|
}
|
||||||
|
|
||||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ metadata {
|
|||||||
|
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
|
capability "Polling"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Switch"
|
capability "Switch"
|
||||||
capability "Switch Level"
|
capability "Switch Level"
|
||||||
@@ -66,6 +67,12 @@ def parse(String description) {
|
|||||||
def resultMap = zigbee.getEvent(description)
|
def resultMap = zigbee.getEvent(description)
|
||||||
if (resultMap) {
|
if (resultMap) {
|
||||||
sendEvent(resultMap)
|
sendEvent(resultMap)
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug "DID NOT PARSE MESSAGE for description : $description"
|
log.debug "DID NOT PARSE MESSAGE for description : $description"
|
||||||
@@ -89,24 +96,27 @@ def setLevel(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.levelRefresh()
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.levelRefresh()
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig()
|
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
def healthPoll() {
|
def poll() {
|
||||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
|
zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||||
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
unschedule()
|
|
||||||
schedule("0 0/5 * * * ? *", "healthPoll")
|
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Device-Watch allows 2 check-in misses from device
|
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||||
// minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
|
||||||
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ def parse(description) {
|
|||||||
log.trace "HUE BRIDGE, GENERATING EVENT: $map.name: $map.value"
|
log.trace "HUE BRIDGE, GENERATING EVENT: $map.name: $map.value"
|
||||||
results << createEvent(name: "${map.name}", value: "${map.value}")
|
results << createEvent(name: "${map.name}", value: "${map.value}")
|
||||||
} else {
|
} else {
|
||||||
log.trace "Parsing description"
|
log.trace "Parsing description"
|
||||||
def msg = parseLanMessage(description)
|
def msg = parseLanMessage(description)
|
||||||
if (msg.body) {
|
if (msg.body) {
|
||||||
def contentType = msg.headers["Content-Type"]
|
def contentType = msg.headers["Content-Type"]
|
||||||
@@ -72,13 +72,13 @@ def parse(description) {
|
|||||||
log.info "Bridge response: $msg.body"
|
log.info "Bridge response: $msg.body"
|
||||||
} else {
|
} else {
|
||||||
// Sending Bulbs List to parent"
|
// Sending Bulbs List to parent"
|
||||||
if (parent.isInBulbDiscovery())
|
if (parent.state.inBulbDiscovery)
|
||||||
log.info parent.bulbListHandler(device.hub.id, msg.body)
|
log.info parent.bulbListHandler(device.hub.id, msg.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (contentType?.contains("xml")) {
|
else if (contentType?.contains("xml")) {
|
||||||
log.debug "HUE BRIDGE ALREADY PRESENT"
|
log.debug "HUE BRIDGE ALREADY PRESENT"
|
||||||
parent.hubVerification(device.hub.id, msg.body)
|
parent.hubVerification(device.hub.id, msg.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ def parse(String description) {
|
|||||||
|
|
||||||
if (descMap.cluster == "0300") {
|
if (descMap.cluster == "0300") {
|
||||||
if(descMap.attrId == "0000"){ //Hue Attribute
|
if(descMap.attrId == "0000"){ //Hue Attribute
|
||||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
|
||||||
log.debug "Hue value returned is $hueValue"
|
log.debug "Hue value returned is $hueValue"
|
||||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ def setLevel(value) {
|
|||||||
|
|
||||||
//input Hue Integer values; returns color name for saturation 100%
|
//input Hue Integer values; returns color name for saturation 100%
|
||||||
private getColorName(hueValue){
|
private getColorName(hueValue){
|
||||||
if(hueValue>100 || hueValue<0)
|
if(hueValue>360 || hueValue<0)
|
||||||
return
|
return
|
||||||
|
|
||||||
hueValue = Math.round(hueValue / 100 * 360)
|
hueValue = Math.round(hueValue / 100 * 360)
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ def parse(String description) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(descMap.attrId == "0000"){ //Hue Attribute
|
else if(descMap.attrId == "0000"){ //Hue Attribute
|
||||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
|
||||||
log.debug "Hue value returned is $hueValue"
|
log.debug "Hue value returned is $hueValue"
|
||||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,7 @@ private getGenericName(value){
|
|||||||
|
|
||||||
//input Hue Integer values; returns color name for saturation 100%
|
//input Hue Integer values; returns color name for saturation 100%
|
||||||
private getColorName(hueValue){
|
private getColorName(hueValue){
|
||||||
if(hueValue>100 || hueValue<0)
|
if(hueValue>360 || hueValue<0)
|
||||||
return
|
return
|
||||||
|
|
||||||
hueValue = Math.round(hueValue / 100 * 360)
|
hueValue = Math.round(hueValue / 100 * 360)
|
||||||
|
|||||||
@@ -101,6 +101,12 @@ def parse(String description) {
|
|||||||
else {
|
else {
|
||||||
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
||||||
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
|
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -120,7 +126,15 @@ def on() {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.onOffRefresh()
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -128,10 +142,8 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// Device-Watch allows 2 check-in misses from device
|
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
zigbee.onOffConfig() + powerConfig() + refresh()
|
||||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
|
||||||
zigbee.onOffConfig(0, 300) + powerConfig() + refresh()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
||||||
|
|||||||
@@ -101,6 +101,13 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
@@ -226,8 +233,6 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
@@ -278,7 +283,15 @@ private Map getMoistureResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -292,19 +305,23 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// Device-Watch allows 2 check-in misses from device
|
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
def enrollCmds = [
|
def configCmds = [
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
]
|
|
||||||
|
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
|
||||||
return enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
|
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
||||||
|
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||||
|
]
|
||||||
|
return configCmds + refresh() // send refresh cmds as part of config
|
||||||
}
|
}
|
||||||
|
|
||||||
def enrollResponse() {
|
def enrollResponse() {
|
||||||
|
|||||||
@@ -105,6 +105,13 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
@@ -241,8 +248,6 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
@@ -289,7 +294,15 @@ private Map getMotionResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -303,19 +316,24 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// Device-Watch allows 2 check-in misses from device
|
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
|
|
||||||
def enrollCmds = [
|
def configCmds = [
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200",
|
||||||
|
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
|
||||||
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||||
|
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200",
|
||||||
|
"zcl global send-me-a-report 0x402 0 0x29 300 3600 {6400}",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
|
||||||
]
|
]
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
return configCmds + refresh() // send refresh cmds as part of config
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
|
||||||
return enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def enrollResponse() {
|
def enrollResponse() {
|
||||||
|
|||||||
@@ -206,8 +206,6 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,13 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
|
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
if (description?.startsWith('enroll request')) {
|
if (description?.startsWith('enroll request')) {
|
||||||
@@ -306,8 +313,6 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
@@ -371,7 +376,15 @@ private getAccelerationResult(numValue) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -401,16 +414,13 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// Device-Watch allows 2 check-in misses from device
|
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
|
||||||
|
|
||||||
log.debug "Configuring Reporting"
|
log.debug "Configuring Reporting"
|
||||||
|
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
|
||||||
def configCmds = enrollResponse() +
|
def configCmds = enrollResponse() +
|
||||||
zigbee.batteryConfig() +
|
zigbee.batteryConfig() +
|
||||||
zigbee.temperatureConfig(30, 300) +
|
zigbee.temperatureConfig() +
|
||||||
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
||||||
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||||
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||||
|
|||||||
@@ -206,8 +206,6 @@ def getTemperature(value) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,13 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(description)
|
map = parseIasMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
@@ -200,8 +207,6 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
@@ -241,7 +246,15 @@ private Map getContactResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -255,19 +268,23 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// Device-Watch allows 2 check-in misses from device
|
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
def enrollCmds = [
|
def configCmds = [
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
]
|
|
||||||
|
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
|
||||||
return enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
|
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
||||||
|
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||||
|
]
|
||||||
|
return configCmds + refresh() // send refresh cmds as part of config
|
||||||
}
|
}
|
||||||
|
|
||||||
def enrollResponse() {
|
def enrollResponse() {
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ def parse(String description) {
|
|||||||
map = parseCustomMessage(description)
|
map = parseCustomMessage(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
|
|
||||||
log.debug "Parse returned $map"
|
log.debug "Parse returned $map"
|
||||||
return map ? createEvent(map) : null
|
return map ? createEvent(map) : null
|
||||||
}
|
}
|
||||||
@@ -207,8 +214,6 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
@@ -246,7 +251,14 @@ private Map getHumidityResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh()
|
def refresh()
|
||||||
@@ -264,19 +276,23 @@ def refresh()
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
// Device-Watch allows 2 check-in misses from device
|
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
|
||||||
|
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
def humidityConfigCmds = [
|
def configCmds = [
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}", "delay 500",
|
||||||
|
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
|
||||||
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
|
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
||||||
|
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
|
||||||
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
|
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
|
||||||
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
|
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||||
]
|
]
|
||||||
|
return configCmds + refresh() // send refresh cmds as part of config
|
||||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
|
||||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
|
||||||
return humidityConfigCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private hex(value) {
|
private hex(value) {
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ def parse(String description) {
|
|||||||
|
|
||||||
def event = zigbee.getEvent(description)
|
def event = zigbee.getEvent(description)
|
||||||
if (event) {
|
if (event) {
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
if (event.name=="level" && event.value==0) {}
|
if (event.name=="level" && event.value==0) {}
|
||||||
else {
|
else {
|
||||||
sendEvent(event)
|
sendEvent(event)
|
||||||
@@ -80,7 +86,15 @@ def setLevel(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.onOffRefresh()
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -89,8 +103,7 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Device-Watch allows 2 check-in misses from device
|
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
||||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||||
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ def parse(String description) {
|
|||||||
def event = zigbee.getEvent(description)
|
def event = zigbee.getEvent(description)
|
||||||
if (event) {
|
if (event) {
|
||||||
log.debug event
|
log.debug event
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
if (event.name=="level" && event.value==0) {}
|
if (event.name=="level" && event.value==0) {}
|
||||||
else {
|
else {
|
||||||
if (event.name=="colorTemperature") {
|
if (event.name=="colorTemperature") {
|
||||||
@@ -99,7 +105,7 @@ def parse(String description) {
|
|||||||
|
|
||||||
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
||||||
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
||||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
|
||||||
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
|
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
|
||||||
}
|
}
|
||||||
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
||||||
@@ -124,7 +130,15 @@ def off() {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.onOffRefresh()
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -133,10 +147,9 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Device-Watch allows 2 check-in misses from device
|
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
||||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01) + zigbee.readAttribute(0x0006, 0x00) + zigbee.readAttribute(0x0008, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||||
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01) + zigbee.readAttribute(0x0006, 0x00) + zigbee.readAttribute(0x0008, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def setColorTemperature(value) {
|
def setColorTemperature(value) {
|
||||||
|
|||||||
@@ -74,6 +74,12 @@ def parse(String description) {
|
|||||||
log.debug "description is $description"
|
log.debug "description is $description"
|
||||||
def event = zigbee.getEvent(description)
|
def event = zigbee.getEvent(description)
|
||||||
if (event) {
|
if (event) {
|
||||||
|
// Temporary fix for the case when Device is OFFLINE and is connected again
|
||||||
|
if (state.lastActivity == null){
|
||||||
|
state.lastActivity = now()
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
state.lastActivity = now()
|
||||||
if (event.name=="level" && event.value==0) {}
|
if (event.name=="level" && event.value==0) {}
|
||||||
else {
|
else {
|
||||||
if (event.name=="colorTemperature") {
|
if (event.name=="colorTemperature") {
|
||||||
@@ -104,7 +110,15 @@ def setLevel(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
return zigbee.onOffRefresh()
|
|
||||||
|
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
||||||
|
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
|
||||||
|
state.lastActivity = null
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
|
} else {
|
||||||
|
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
|
||||||
|
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -113,10 +127,9 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Device-Watch allows 2 check-in misses from device
|
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
||||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
||||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||||
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def setColorTemperature(value) {
|
def setColorTemperature(value) {
|
||||||
|
|||||||
@@ -65,16 +65,7 @@ void updateSwitch() {
|
|||||||
private void updateAll(devices) {
|
private void updateAll(devices) {
|
||||||
def command = request.JSON?.command
|
def command = request.JSON?.command
|
||||||
if (command) {
|
if (command) {
|
||||||
switch(command) {
|
devices."$command"()
|
||||||
case "on":
|
|
||||||
devices.on()
|
|
||||||
break
|
|
||||||
case "off":
|
|
||||||
devices.off()
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
httpError(403, "Access denied. This command is not supported by current capability.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,16 +77,7 @@ private void update(devices) {
|
|||||||
if (!device) {
|
if (!device) {
|
||||||
httpError(404, "Device not found")
|
httpError(404, "Device not found")
|
||||||
} else {
|
} else {
|
||||||
switch(command) {
|
device."$command"()
|
||||||
case "on":
|
|
||||||
device.on()
|
|
||||||
break
|
|
||||||
case "off":
|
|
||||||
device.off()
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
httpError(403, "Access denied. This command is not supported by current capability.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def authPage() {
|
|||||||
if (canInstallLabs()) {
|
if (canInstallLabs()) {
|
||||||
|
|
||||||
def redirectUrl = getBuildRedirectUrl()
|
def redirectUrl = getBuildRedirectUrl()
|
||||||
// log.debug "Redirect url = ${redirectUrl}"
|
log.debug "Redirect url = ${redirectUrl}"
|
||||||
|
|
||||||
if (state.authToken) {
|
if (state.authToken) {
|
||||||
description = "Tap 'Next' to proceed"
|
description = "Tap 'Next' to proceed"
|
||||||
@@ -113,13 +113,13 @@ def oauthInitUrl() {
|
|||||||
scope: "read_station"
|
scope: "read_station"
|
||||||
]
|
]
|
||||||
|
|
||||||
// log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
|
log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
|
||||||
|
|
||||||
redirect (location: getVendorAuthPath() + toQueryString(oauthParams))
|
redirect (location: getVendorAuthPath() + toQueryString(oauthParams))
|
||||||
}
|
}
|
||||||
|
|
||||||
def callback() {
|
def callback() {
|
||||||
// log.debug "callback()>> params: $params, params.code ${params.code}"
|
log.debug "callback()>> params: $params, params.code ${params.code}"
|
||||||
|
|
||||||
def code = params.code
|
def code = params.code
|
||||||
def oauthState = params.state
|
def oauthState = params.state
|
||||||
@@ -135,7 +135,7 @@ def callback() {
|
|||||||
scope: "read_station"
|
scope: "read_station"
|
||||||
]
|
]
|
||||||
|
|
||||||
// log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
|
log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
|
||||||
|
|
||||||
def tokenUrl = getVendorTokenPath()
|
def tokenUrl = getVendorTokenPath()
|
||||||
def params = [
|
def params = [
|
||||||
@@ -144,7 +144,7 @@ def callback() {
|
|||||||
body: tokenParams
|
body: tokenParams
|
||||||
]
|
]
|
||||||
|
|
||||||
// log.debug "PARAMS: ${params}"
|
log.debug "PARAMS: ${params}"
|
||||||
|
|
||||||
httpPost(params) { resp ->
|
httpPost(params) { resp ->
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ def callback() {
|
|||||||
state.refreshToken = data.refresh_token
|
state.refreshToken = data.refresh_token
|
||||||
state.authToken = data.access_token
|
state.authToken = data.access_token
|
||||||
state.tokenExpires = now() + (data.expires_in * 1000)
|
state.tokenExpires = now() + (data.expires_in * 1000)
|
||||||
// log.debug "swapped token: $resp.data"
|
log.debug "swapped token: $resp.data"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +292,7 @@ def refreshToken() {
|
|||||||
|
|
||||||
response.data.each {key, value ->
|
response.data.each {key, value ->
|
||||||
def data = slurper.parseText(key);
|
def data = slurper.parseText(key);
|
||||||
// log.debug "Data: $data"
|
log.debug "Data: $data"
|
||||||
|
|
||||||
state.refreshToken = data.refresh_token
|
state.refreshToken = data.refresh_token
|
||||||
state.accessToken = data.access_token
|
state.accessToken = data.access_token
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ mappings {
|
|||||||
path("/receivedToken") { action: [ POST: "receivedToken", GET: "receivedToken"] }
|
path("/receivedToken") { action: [ POST: "receivedToken", GET: "receivedToken"] }
|
||||||
path("/receiveToken") { action: [ POST: "receiveToken", GET: "receiveToken"] }
|
path("/receiveToken") { action: [ POST: "receiveToken", GET: "receiveToken"] }
|
||||||
path("/hookCallback") { action: [ POST: "hookEventHandler", GET: "hookEventHandler"] }
|
path("/hookCallback") { action: [ POST: "hookEventHandler", GET: "hookEventHandler"] }
|
||||||
path("/oauth/initialize") {action: [GET: "oauthInitUrl"]}
|
path("/oauth/initialize") {action: [GET: "oauthInitUrl"]}
|
||||||
path("/oauth/callback") { action: [ GET: "callback" ] }
|
path("/oauth/callback") { action: [ GET: "callback" ] }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,6 @@ def authPage() {
|
|||||||
log.debug "RedirectURL = ${redirectUrl}"
|
log.debug "RedirectURL = ${redirectUrl}"
|
||||||
def donebutton= state.JawboneAccessToken != null
|
def donebutton= state.JawboneAccessToken != null
|
||||||
return dynamicPage(name: "Credentials", title: "Jawbone UP", nextPage: null, uninstall: true, install: donebutton) {
|
return dynamicPage(name: "Credentials", title: "Jawbone UP", nextPage: null, uninstall: true, install: donebutton) {
|
||||||
section { paragraph title: "Note:", "This device has not been officially tested and certified to “Work with SmartThings”. You can connect it to your SmartThings home but performance may vary and we will not be able to provide support or assistance." }
|
|
||||||
section { href url:redirectUrl, style:"embedded", required:true, title:"Jawbone UP", state: hast ,description:description }
|
section { href url:redirectUrl, style:"embedded", required:true, title:"Jawbone UP", state: hast ,description:description }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -235,7 +234,7 @@ def validateCurrentToken() {
|
|||||||
httpPost(uri: url, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], body: requestBody) {response ->
|
httpPost(uri: url, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], body: requestBody) {response ->
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
log.debug "${response.data}"
|
log.debug "${response.data}"
|
||||||
log.debug "Setting refresh token"
|
log.debug "Setting refresh token to ${response.data.data.refresh_token}"
|
||||||
state.refreshToken = response.data.data.refresh_token
|
state.refreshToken = response.data.data.refresh_token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,7 +258,7 @@ def validateCurrentToken() {
|
|||||||
state.remove("refreshToken")
|
state.remove("refreshToken")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug "Setting access token"
|
log.debug "Setting access token to ${data.access_token}, refresh token to ${data.refresh_token}"
|
||||||
state.JawboneAccessToken = data.access_token
|
state.JawboneAccessToken = data.access_token
|
||||||
state.refreshToken = data.refresh_token
|
state.refreshToken = data.refresh_token
|
||||||
}
|
}
|
||||||
@@ -467,7 +466,7 @@ def hookEventHandler() {
|
|||||||
moves = response.data.data.items[0]
|
moves = response.data.data.items[0]
|
||||||
}
|
}
|
||||||
log.debug "Goal = ${goals.move_steps} Steps"
|
log.debug "Goal = ${goals.move_steps} Steps"
|
||||||
log.debug "Steps = ${moves.details.steps} Steps"
|
log.debug "Steps = ${moves.details.steps} Steps"
|
||||||
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
||||||
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
||||||
//setColor(moves.details.steps,goals.move_steps,childDevice)
|
//setColor(moves.details.steps,goals.move_steps,childDevice)
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ def installed() {
|
|||||||
def updated() {
|
def updated() {
|
||||||
unschedule()
|
unschedule()
|
||||||
turnOffSwitch() //Turn off all switches if the schedules are changed while in mid-schedule
|
turnOffSwitch() //Turn off all switches if the schedules are changed while in mid-schedule
|
||||||
unsubscribe()
|
unsubscribe
|
||||||
log.debug "Updated with settings: ${settings}"
|
log.debug "Updated with settings: ${settings}"
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ definition(
|
|||||||
name: "Monitor on Sense",
|
name: "Monitor on Sense",
|
||||||
namespace: "resteele",
|
namespace: "resteele",
|
||||||
author: "Rachel Steele",
|
author: "Rachel Steele",
|
||||||
description: "Turn on switch when vibration is sensed",
|
description: "Turn on Monitor when vibration is sensed",
|
||||||
category: "My Apps",
|
category: "My Apps",
|
||||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||||
@@ -25,10 +25,10 @@ definition(
|
|||||||
|
|
||||||
|
|
||||||
preferences {
|
preferences {
|
||||||
section("When vibration is sensed...") {
|
section("When the keyboard is used...") {
|
||||||
input "accelerationSensor", "capability.accelerationSensor", title: "Which Sensor?"
|
input "accelerationSensor", "capability.accelerationSensor", title: "Which Sensor?"
|
||||||
}
|
}
|
||||||
section("Turn on switch...") {
|
section("Turn on/off a light...") {
|
||||||
input "switch1", "capability.switch"
|
input "switch1", "capability.switch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,3 +47,5 @@ def updated() {
|
|||||||
def accelerationActiveHandler(evt) {
|
def accelerationActiveHandler(evt) {
|
||||||
switch1.on()
|
switch1.on()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -114,16 +114,13 @@ def beaconHandler(evt) {
|
|||||||
|
|
||||||
if (allOk) {
|
if (allOk) {
|
||||||
def data = new groovy.json.JsonSlurper().parseText(evt.data)
|
def data = new groovy.json.JsonSlurper().parseText(evt.data)
|
||||||
// removed logging of device names. can be added back for debugging
|
log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
|
||||||
//log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
|
|
||||||
|
|
||||||
def beaconName = getBeaconName(evt)
|
def beaconName = getBeaconName(evt)
|
||||||
// removed logging of device names. can be added back for debugging
|
log.debug "<beacon-control> beaconName: $beaconName"
|
||||||
//log.debug "<beacon-control> beaconName: $beaconName"
|
|
||||||
|
|
||||||
def phoneName = getPhoneName(data)
|
def phoneName = getPhoneName(data)
|
||||||
// removed logging of device names. can be added back for debugging
|
log.debug "<beacon-control> phoneName: $phoneName"
|
||||||
//log.debug "<beacon-control> phoneName: $phoneName"
|
|
||||||
if (phoneName != null) {
|
if (phoneName != null) {
|
||||||
def action = data.presence == "1" ? "arrived" : "left"
|
def action = data.presence == "1" ? "arrived" : "left"
|
||||||
def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"
|
def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"
|
||||||
|
|||||||
@@ -49,15 +49,13 @@ preferences {
|
|||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
log.debug "Installed with settings: ${settings}"
|
log.debug "Installed with settings: ${settings}"
|
||||||
// commented out log statement because presence sensor label could contain user's name
|
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
||||||
//log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
|
||||||
subscribe(people, "presence", presence)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
log.debug "Updated with settings: ${settings}"
|
log.debug "Updated with settings: ${settings}"
|
||||||
// commented out log statement because presence sensor label could contain user's name
|
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
||||||
//log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
subscribe(people, "presence", presence)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,12 +64,10 @@ def meterHandler(evt) {
|
|||||||
def lastValue = atomicState.lastValue as double
|
def lastValue = atomicState.lastValue as double
|
||||||
atomicState.lastValue = meterValue
|
atomicState.lastValue = meterValue
|
||||||
|
|
||||||
def dUnit = evt.unit ?: "Watts"
|
|
||||||
|
|
||||||
def aboveThresholdValue = aboveThreshold as int
|
def aboveThresholdValue = aboveThreshold as int
|
||||||
if (meterValue > aboveThresholdValue) {
|
if (meterValue > aboveThresholdValue) {
|
||||||
if (lastValue < aboveThresholdValue) { // only send notifications when crossing the threshold
|
if (lastValue < aboveThresholdValue) { // only send notifications when crossing the threshold
|
||||||
def msg = "${meter} reported ${evt.value} ${dUnit} which is above your threshold of ${aboveThreshold}."
|
def msg = "${meter} reported ${evt.value} ${evt.unit} which is above your threshold of ${aboveThreshold}."
|
||||||
sendMessage(msg)
|
sendMessage(msg)
|
||||||
} else {
|
} else {
|
||||||
// log.debug "not sending notification for ${evt.description} because the threshold (${aboveThreshold}) has already been crossed"
|
// log.debug "not sending notification for ${evt.description} because the threshold (${aboveThreshold}) has already been crossed"
|
||||||
@@ -80,7 +78,7 @@ def meterHandler(evt) {
|
|||||||
def belowThresholdValue = belowThreshold as int
|
def belowThresholdValue = belowThreshold as int
|
||||||
if (meterValue < belowThresholdValue) {
|
if (meterValue < belowThresholdValue) {
|
||||||
if (lastValue > belowThresholdValue) { // only send notifications when crossing the threshold
|
if (lastValue > belowThresholdValue) { // only send notifications when crossing the threshold
|
||||||
def msg = "${meter} reported ${evt.value} ${dUnit} which is below your threshold of ${belowThreshold}."
|
def msg = "${meter} reported ${evt.value} ${evt.unit} which is below your threshold of ${belowThreshold}."
|
||||||
sendMessage(msg)
|
sendMessage(msg)
|
||||||
} else {
|
} else {
|
||||||
// log.debug "not sending notification for ${evt.description} because the threshold (${belowThreshold}) has already been crossed"
|
// log.debug "not sending notification for ${evt.description} because the threshold (${belowThreshold}) has already been crossed"
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ def waterWetHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "wet" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "wet" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
if (alreadySentSms) {
|
||||||
log.debug "SMS already sent within the last $deltaSeconds seconds"
|
log.debug "SMS already sent to $phone within the last $deltaSeconds seconds"
|
||||||
} else {
|
} else {
|
||||||
def msg = "${alarm.displayName} is wet!"
|
def msg = "${alarm.displayName} is wet!"
|
||||||
log.debug "$alarm is wet, texting phone number"
|
log.debug "$alarm is wet, texting $phone"
|
||||||
|
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts(msg, recipients)
|
sendNotificationToContacts(msg, recipients)
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ def takeAction(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
def sendTextMessage() {
|
def sendTextMessage() {
|
||||||
log.debug "$multisensor was open too long, texting phone"
|
log.debug "$multisensor was open too long, texting $phone"
|
||||||
|
|
||||||
updateSmsHistory()
|
updateSmsHistory()
|
||||||
def openMinutes = maxOpenTime * (state.smsHistory?.size() ?: 1)
|
def openMinutes = maxOpenTime * (state.smsHistory?.size() ?: 1)
|
||||||
|
|||||||
@@ -761,7 +761,7 @@ String displayableTime(timeRemaining) {
|
|||||||
return "${minutes}:00"
|
return "${minutes}:00"
|
||||||
}
|
}
|
||||||
def fraction = "0.${parts[1]}" as double
|
def fraction = "0.${parts[1]}" as double
|
||||||
def seconds = "${60 * fraction as int}".padLeft(2, "0")
|
def seconds = "${60 * fraction as int}".padRight(2, "0")
|
||||||
return "${minutes}:${seconds}"
|
return "${minutes}:${seconds}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ preferences {
|
|||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
log.debug "Installed with settings: ${settings}"
|
log.debug "Installed with settings: ${settings}"
|
||||||
// log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
||||||
subscribe(people, "presence", presence)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
log.debug "Updated with settings: ${settings}"
|
log.debug "Updated with settings: ${settings}"
|
||||||
// log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
subscribe(people, "presence", presence)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
@@ -71,10 +71,11 @@ def presence(evt)
|
|||||||
def person = getPerson(evt)
|
def person = getPerson(evt)
|
||||||
def recentNotPresent = person.statesSince("presence", t0).find{it.value == "not present"}
|
def recentNotPresent = person.statesSince("presence", t0).find{it.value == "not present"}
|
||||||
if (recentNotPresent) {
|
if (recentNotPresent) {
|
||||||
log.debug "skipping notification of arrival of Person because last departure was only ${now() - recentNotPresent.date.time} msec ago"
|
log.debug "skipping notification of arrival of ${person.displayName} because last departure was only ${now() - recentNotPresent.date.time} msec ago"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
def message = "${person.displayName} arrived at home, changing mode to '${newMode}'"
|
def message = "${person.displayName} arrived at home, changing mode to '${newMode}'"
|
||||||
|
log.info message
|
||||||
send(message)
|
send(message)
|
||||||
setLocationMode(newMode)
|
setLocationMode(newMode)
|
||||||
}
|
}
|
||||||
@@ -105,4 +106,6 @@ private send(msg) {
|
|||||||
sendSms(phone, msg)
|
sendSms(phone, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug msg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,11 +57,12 @@ def scheduleCheck()
|
|||||||
def message = message1 ?: "SmartThings - Habit Helper Reminder!"
|
def message = message1 ?: "SmartThings - Habit Helper Reminder!"
|
||||||
|
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
log.debug "Texting reminder to contacts:${recipients?.size()}"
|
log.debug "Texting reminder: ($message) to contacts:${recipients?.size()}"
|
||||||
sendNotificationToContacts(message, recipients)
|
sendNotificationToContacts(message, recipients)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug "Texting reminder"
|
|
||||||
|
log.debug "Texting reminder: ($message) to $phone1"
|
||||||
sendSms(phone1, message)
|
sendSms(phone1, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ def bulbDiscovery() {
|
|||||||
|
|
||||||
return dynamicPage(name:"bulbDiscovery", title:"Light Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) {
|
return dynamicPage(name:"bulbDiscovery", title:"Light Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) {
|
||||||
section("Please wait while we discover your Hue Lights. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") {
|
section("Please wait while we discover your Hue Lights. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") {
|
||||||
input "selectedBulbs", "enum", required:false, title:"Select Hue Lights to add (${numFound} found)", multiple:true, submitOnChange: true, options:newLights
|
input "selectedBulbs", "enum", required:false, title:"Select Hue Lights to add (${numFound} found)", multiple:true, options:newLights
|
||||||
paragraph title: "Previously added Hue Lights (${existingLights.size()} added)", existingLightsDescription
|
paragraph title: "Previously added Hue Lights (${existingLights.size()} added)", existingLightsDescription
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
@@ -724,13 +724,13 @@ private void updateBridgeStatus(childDevice) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if all Hue bridges have been heard from in the last 11 minutes, if not an Offline event will be sent
|
* Check if all Hue bridges have been heard from in the last 16 minutes, if not an Offline event will be sent
|
||||||
* for the bridge and all connected lights. Also, set ID number on bridge if not done previously.
|
* for the bridge. Also, set ID number on bridge if not done previously.
|
||||||
*/
|
*/
|
||||||
private void checkBridgeStatus() {
|
private void checkBridgeStatus() {
|
||||||
def bridges = getHueBridges()
|
def bridges = getHueBridges()
|
||||||
// Check if each bridge has been heard from within the last 11 minutes (2 poll intervals times 5 minutes plus buffer)
|
// Check if each bridge has been heard from within the last 16 minutes (3 poll intervals times 5 minutes plus buffer)
|
||||||
def time = now() - (1000 * 60 * 11)
|
def time = now() - (1000 * 60 * 30)
|
||||||
bridges.each {
|
bridges.each {
|
||||||
def d = getChildDevice(it.value.mac)
|
def d = getChildDevice(it.value.mac)
|
||||||
if(d) {
|
if(d) {
|
||||||
@@ -740,21 +740,16 @@ private void checkBridgeStatus() {
|
|||||||
d.sendEvent(name: "idNumber", value: it.value.idNumber)
|
d.sendEvent(name: "idNumber", value: it.value.idNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
|
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
|
||||||
log.warn "Bridge $it.key is Offline"
|
log.warn "Bridge $it.key is Offline"
|
||||||
d.sendEvent(name: "status", value: "Offline")
|
d.sendEvent(name: "status", value: "Offline")
|
||||||
|
// set all lights to offline since bridge is not reachable
|
||||||
state.bulbs?.each {
|
state.bulbs?.each {it.value.online = false}
|
||||||
it.value.online = false
|
} else {
|
||||||
}
|
|
||||||
getChildDevices().each {
|
|
||||||
it.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", isStateChange: true, displayed: false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
d.sendEvent(name: "status", value: "Online")//setOnline(false)
|
d.sendEvent(name: "status", value: "Online")//setOnline(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def isValidSource(macAddress) {
|
def isValidSource(macAddress) {
|
||||||
@@ -762,10 +757,6 @@ def isValidSource(macAddress) {
|
|||||||
return (vbridges?.find {"${it.value.mac}" == macAddress}) != null
|
return (vbridges?.find {"${it.value.mac}" == macAddress}) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
def isInBulbDiscovery() {
|
|
||||||
return state.inBulbDiscovery
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
//CHILD DEVICE METHODS
|
//CHILD DEVICE METHODS
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
@@ -801,12 +792,10 @@ def parse(childDevice, description) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Philips Hue priority for color is xy > ct > hs
|
// Philips Hue priority for color is xy > ct > hs
|
||||||
// For SmartThings, try to always send hue, sat and hex
|
|
||||||
private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
||||||
if (device == null || (xy == null && hue == null && sat == null && ct == null))
|
if (device == null || (xy == null && hue == null && sat == null && ct == null))
|
||||||
return
|
return
|
||||||
|
|
||||||
def events = [:]
|
|
||||||
// For now, only care about changing color temperature if requested by user
|
// For now, only care about changing color temperature if requested by user
|
||||||
if (ct != null && (colormode == "ct" || (xy == null && hue == null && sat == null))) {
|
if (ct != null && (colormode == "ct" || (xy == null && hue == null && sat == null))) {
|
||||||
// for some reason setting Hue to their specified minimum off 153 yields 154, dealt with below
|
// for some reason setting Hue to their specified minimum off 153 yields 154, dealt with below
|
||||||
@@ -820,13 +809,13 @@ private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
|||||||
if (hue != null) {
|
if (hue != null) {
|
||||||
// 0-65535
|
// 0-65535
|
||||||
def value = Math.min(Math.round(hue * 100 / 65535), 65535) as int
|
def value = Math.min(Math.round(hue * 100 / 65535), 65535) as int
|
||||||
events["hue"] = [name: "hue", value: value, descriptionText: "Color has changed", displayed: false]
|
device.sendEvent([name: "hue", value: value, descriptionText: "Color has changed"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sat != null) {
|
if (sat != null) {
|
||||||
// 0-254
|
// 0-254
|
||||||
def value = Math.round(sat * 100 / 254) as int
|
def value = Math.round(sat * 100 / 254) as int
|
||||||
events["saturation"] = [name: "saturation", value: value, descriptionText: "Color has changed", displayed: false]
|
device.sendEvent([name: "saturation", value: value, descriptionText: "Color has changed"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following is used to decide what to base hex calculations on since it is preferred to return a colorchange in hex
|
// Following is used to decide what to base hex calculations on since it is preferred to return a colorchange in hex
|
||||||
@@ -838,28 +827,17 @@ private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
|||||||
def model = state.bulbs[id]?.modelid
|
def model = state.bulbs[id]?.modelid
|
||||||
def hex = colorFromXY(xy, model)
|
def hex = colorFromXY(xy, model)
|
||||||
|
|
||||||
// Create Hue and Saturation events if not previously existing
|
// TODO Disabled until a solution for the jumping color picker can be figured out
|
||||||
def hsv = hexToHsv(hex)
|
//device.sendEvent([name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed", displayed: false])
|
||||||
if (events["hue"] == null)
|
|
||||||
events["hue"] = [name: "hue", value: hsv[0], descriptionText: "Color has changed", displayed: false]
|
|
||||||
if (events["saturation"] == null)
|
|
||||||
events["saturation"] = [name: "saturation", value: hsv[1], descriptionText: "Color has changed", displayed: false]
|
|
||||||
|
|
||||||
events["color"] = [name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed", displayed: true]
|
|
||||||
} else if (colormode == "hs" || colormode == null) {
|
} else if (colormode == "hs" || colormode == null) {
|
||||||
// colormode is "hs" or "xy" is missing, default to follow hue/sat which is already handled above
|
// colormode is "hs" or "xy" is missing, default to follow hue/sat which is already handled above
|
||||||
def hueValue = (hue != null) ? events["hue"].value : Integer.parseInt("$device.currentHue")
|
|
||||||
def satValue = (sat != null) ? events["saturation"].value : Integer.parseInt("$device.currentSaturation")
|
|
||||||
|
|
||||||
|
// TODO Disabled until the standard behavior of lights is defined (hue and sat events are sent above)
|
||||||
def hex = hsvToHex(hueValue, satValue)
|
//def hex = colorUtil.hslToHex((int) device.currentHue, (int) device.currentSaturation)
|
||||||
events["color"] = [name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed", displayed: true]
|
// device.sendEvent([name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed"])
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean sendColorChanged = false
|
return debug
|
||||||
events.each {
|
|
||||||
device.sendEvent(it.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendBasicEvents(device, param, value) {
|
private sendBasicEvents(device, param, value) {
|
||||||
@@ -900,6 +878,8 @@ private handleCommandResponse(body) {
|
|||||||
def updates = [:]
|
def updates = [:]
|
||||||
|
|
||||||
body.each { payload ->
|
body.each { payload ->
|
||||||
|
log.debug $payload
|
||||||
|
|
||||||
if (payload?.success) {
|
if (payload?.success) {
|
||||||
def childDeviceNetworkId = app.id + "/"
|
def childDeviceNetworkId = app.id + "/"
|
||||||
def eventType
|
def eventType
|
||||||
@@ -971,7 +951,6 @@ private handlePoll(body) {
|
|||||||
} else {
|
} else {
|
||||||
state.bulbs[bulb.key]?.online = false
|
state.bulbs[bulb.key]?.online = false
|
||||||
log.warn "$device is not reachable by Hue bridge"
|
log.warn "$device is not reachable by Hue bridge"
|
||||||
device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1112,22 +1091,26 @@ def setColor(childDevice, huesettings) {
|
|||||||
def sat = null
|
def sat = null
|
||||||
def xy = null
|
def xy = null
|
||||||
|
|
||||||
// Prefer hue/sat over hex to make sure it works with the majority of the smartapps
|
// For now ignore model to get a consistent color if same color is set across multiple devices
|
||||||
if (huesettings.hue != null || huesettings.sat != null) {
|
// def model = state.bulbs[getId(childDevice)]?.modelid
|
||||||
// If both hex and hue/sat are set, send all values to bridge to get hue/sat in response from bridge to
|
if (huesettings.hex != null) {
|
||||||
// generate hue/sat events even though bridge will prioritize XY when setting color
|
|
||||||
if (huesettings.hue != null)
|
|
||||||
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
|
|
||||||
if (huesettings.saturation != null)
|
|
||||||
value.sat = Math.min(Math.round(huesettings.saturation * 254 / 100), 254)
|
|
||||||
} else if (huesettings.hex != null && false) {
|
|
||||||
// For now ignore model to get a consistent color if same color is set across multiple devices
|
|
||||||
// def model = state.bulbs[getId(childDevice)]?.modelid
|
|
||||||
// value.xy = calculateXY(huesettings.hex, model)
|
// value.xy = calculateXY(huesettings.hex, model)
|
||||||
// Once groups, or scenes are introduced it might be a good idea to use unique models again
|
// Once groups, or scenes are introduced it might be a good idea to use unique models again
|
||||||
value.xy = calculateXY(huesettings.hex)
|
value.xy = calculateXY(huesettings.hex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If both hex and hue/sat are set, send all values to bridge to get hue/sat in response from bridge to
|
||||||
|
// generate hue/sat events even though bridge will prioritize XY when setting color
|
||||||
|
if (huesettings.hue != null)
|
||||||
|
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
|
||||||
|
else
|
||||||
|
value.hue = Math.min(Math.round(childDevice.device?.currentValue("hue") * 65535 / 100), 65535)
|
||||||
|
|
||||||
|
if (huesettings.saturation != null)
|
||||||
|
value.sat = Math.min(Math.round(huesettings.saturation * 254 / 100), 254)
|
||||||
|
else
|
||||||
|
value.sat = Math.min(Math.round(childDevice.device?.currentValue("saturation") * 254 / 100), 254)
|
||||||
|
|
||||||
/* Disabled for now due to bad behavior via Lightning Wizard
|
/* Disabled for now due to bad behavior via Lightning Wizard
|
||||||
if (!value.xy) {
|
if (!value.xy) {
|
||||||
// Below will translate values to hex->XY to take into account the color support of the different hue types
|
// Below will translate values to hex->XY to take into account the color support of the different hue types
|
||||||
@@ -1182,8 +1165,9 @@ private poll() {
|
|||||||
def host = getBridgeIP()
|
def host = getBridgeIP()
|
||||||
def uri = "/api/${state.username}/lights/"
|
def uri = "/api/${state.username}/lights/"
|
||||||
log.debug "GET: $host$uri"
|
log.debug "GET: $host$uri"
|
||||||
sendHubCommand(new physicalgraph.device.HubAction("GET ${uri} HTTP/1.1\r\n" +
|
sendHubCommand(new physicalgraph.device.HubAction("""GET ${uri} HTTP/1.1
|
||||||
"HOST: ${host}\r\n\r\n", physicalgraph.device.Protocol.LAN, selectedHue))
|
HOST: ${host}
|
||||||
|
""", physicalgraph.device.Protocol.LAN, selectedHue))
|
||||||
}
|
}
|
||||||
|
|
||||||
private isOnline(id) {
|
private isOnline(id) {
|
||||||
@@ -1199,11 +1183,13 @@ private put(path, body) {
|
|||||||
log.debug "PUT: $host$uri"
|
log.debug "PUT: $host$uri"
|
||||||
log.debug "BODY: ${bodyJSON}"
|
log.debug "BODY: ${bodyJSON}"
|
||||||
|
|
||||||
sendHubCommand(new physicalgraph.device.HubAction("PUT $uri HTTP/1.1\r\n" +
|
sendHubCommand(new physicalgraph.device.HubAction("""PUT $uri HTTP/1.1
|
||||||
"HOST: ${host}\r\n" +
|
HOST: ${host}
|
||||||
"Content-Length: ${length}\r\n" +
|
Content-Length: ${length}
|
||||||
"\r\n" +
|
|
||||||
"${bodyJSON}", physicalgraph.device.Protocol.LAN, "${selectedHue}"))
|
${bodyJSON}
|
||||||
|
""", physicalgraph.device.Protocol.LAN, "${selectedHue}"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1661,101 +1647,3 @@ private boolean checkPointInLampsReach(p, colorPoints) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts an RGB color in hex to HSV.
|
|
||||||
* Algorithm based on http://en.wikipedia.org/wiki/HSV_color_space.
|
|
||||||
*
|
|
||||||
* @param colorStr color value in hex (#ff03d3)
|
|
||||||
*
|
|
||||||
* @return HSV representation in an array (0-100) [hue, sat, value]
|
|
||||||
*/
|
|
||||||
def hexToHsv(colorStr){
|
|
||||||
def r = Integer.valueOf( colorStr.substring( 1, 3 ), 16 ) / 255
|
|
||||||
def g = Integer.valueOf( colorStr.substring( 3, 5 ), 16 ) / 255
|
|
||||||
def b = Integer.valueOf( colorStr.substring( 5, 7 ), 16 ) / 255;
|
|
||||||
|
|
||||||
def max = Math.max(Math.max(r, g), b)
|
|
||||||
def min = Math.min(Math.min(r, g), b)
|
|
||||||
|
|
||||||
def h, s, v = max;
|
|
||||||
|
|
||||||
def d = max - min;
|
|
||||||
s = max == 0 ? 0 : d / max;
|
|
||||||
|
|
||||||
if(max == min){
|
|
||||||
h = 0;
|
|
||||||
}else{
|
|
||||||
switch(max){
|
|
||||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
|
||||||
case g: h = (b - r) / d + 2; break;
|
|
||||||
case b: h = (r - g) / d + 4; break;
|
|
||||||
}
|
|
||||||
h /= 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [(h * 100).round(), (s * 100).round(), (v * 100).round()];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts HSV color to RGB in hex.
|
|
||||||
* Algorithm based on http://en.wikipedia.org/wiki/HSV_color_space.
|
|
||||||
*
|
|
||||||
* @param hue hue 0-100
|
|
||||||
* @param sat saturation 0-100
|
|
||||||
* @param value value 0-100 (defaults to 100)
|
|
||||||
|
|
||||||
* @return the color in hex (#ff03d3)
|
|
||||||
*/
|
|
||||||
def hsvToHex(hue, sat, value = 100){
|
|
||||||
def r, g, b;
|
|
||||||
def h = hue / 100
|
|
||||||
def s = sat / 100
|
|
||||||
def v = value / 100
|
|
||||||
|
|
||||||
def i = Math.floor(h * 6);
|
|
||||||
def f = h * 6 - i;
|
|
||||||
def p = v * (1 - s);
|
|
||||||
def q = v * (1 - f * s);
|
|
||||||
def t = v * (1 - (1 - f) * s);
|
|
||||||
|
|
||||||
switch (i % 6) {
|
|
||||||
case 0:
|
|
||||||
r = v
|
|
||||||
g = t
|
|
||||||
b = p
|
|
||||||
break
|
|
||||||
case 1:
|
|
||||||
r = q
|
|
||||||
g = v
|
|
||||||
b = p
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
r = p
|
|
||||||
g = v
|
|
||||||
b = t
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
r = p
|
|
||||||
g = q
|
|
||||||
b = v
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
r = t
|
|
||||||
g = p
|
|
||||||
b = v
|
|
||||||
break
|
|
||||||
case 5:
|
|
||||||
r = v
|
|
||||||
g = p
|
|
||||||
b = q
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converting float components to int components.
|
|
||||||
def r1 = String.format("%02X", (int) (r * 255.0f));
|
|
||||||
def g1 = String.format("%02X", (int) (g * 255.0f));
|
|
||||||
def b1 = String.format("%02X", (int) (b * 255.0f));
|
|
||||||
|
|
||||||
return "#$r1$g1$b1"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -53,14 +53,14 @@ def accelerationActiveHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
if (alreadySentSms) {
|
||||||
log.debug "SMS already sent within the last $deltaSeconds seconds"
|
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
|
||||||
} else {
|
} else {
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
log.debug "accelerationSensor has moved, texting contacts: ${recipients?.size()}"
|
log.debug "$accelerationSensor has moved, texting contacts: ${recipients?.size()}"
|
||||||
sendNotificationToContacts("${accelerationSensor.label ?: accelerationSensor.name} moved", recipients)
|
sendNotificationToContacts("${accelerationSensor.label ?: accelerationSensor.name} moved", recipients)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug "accelerationSensor has moved, sending text message"
|
log.debug "$accelerationSensor has moved, texting $phone1"
|
||||||
sendSms(phone1, "${accelerationSensor.label ?: accelerationSensor.name} moved")
|
sendSms(phone1, "${accelerationSensor.label ?: accelerationSensor.name} moved")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ def temperatureHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1
|
def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
if (alreadySentSms) {
|
||||||
log.debug "SMS already sent within the last $deltaMinutes minutes"
|
log.debug "SMS already sent to $phone1 within the last $deltaMinutes minutes"
|
||||||
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
||||||
} else {
|
} else {
|
||||||
log.debug "Temperature rose above $tooHot: sending SMS and activating $mySwitch"
|
log.debug "Temperature rose above $tooHot: sending SMS to $phone1 and activating $mySwitch"
|
||||||
def tempScale = location.temperatureScale ?: "F"
|
def tempScale = location.temperatureScale ?: "F"
|
||||||
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
|
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
|
||||||
switch1?.on()
|
switch1?.on()
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ def authPage() {
|
|||||||
}
|
}
|
||||||
def description = "Tap to enter LIFX credentials"
|
def description = "Tap to enter LIFX credentials"
|
||||||
def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" // this triggers oauthInit() below
|
def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" // this triggers oauthInit() below
|
||||||
// def redirectUrl = "${apiServerUrl}"
|
// def redirectUrl = "${apiServerUrl}"
|
||||||
// log.debug "app id: ${app.id}"
|
log.debug "app id: ${app.id}"
|
||||||
// log.debug "redirect url: ${redirectUrl}"s
|
log.debug "redirect url: ${redirectUrl}"
|
||||||
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:true) {
|
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:true) {
|
||||||
section {
|
section {
|
||||||
href(url:redirectUrl, required:true, title:"Connect to LIFX", description:"Tap here to connect your LIFX account")
|
href(url:redirectUrl, required:true, title:"Connect to LIFX", description:"Tap here to connect your LIFX account")
|
||||||
@@ -372,7 +372,7 @@ def updateDevices() {
|
|||||||
def childDevice = getChildDevice(device.id)
|
def childDevice = getChildDevice(device.id)
|
||||||
selectors.add("${device.id}")
|
selectors.add("${device.id}")
|
||||||
if (!childDevice) {
|
if (!childDevice) {
|
||||||
// log.info("Adding device ${device.id}: ${device.product}")
|
log.info("Adding device ${device.id}: ${device.product}")
|
||||||
def data = [
|
def data = [
|
||||||
label: device.label,
|
label: device.label,
|
||||||
level: Math.round((device.brightness ?: 1) * 100),
|
level: Math.round((device.brightness ?: 1) * 100),
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ definition(
|
|||||||
}
|
}
|
||||||
|
|
||||||
preferences(oauthPage: "deviceAuthorization") {
|
preferences(oauthPage: "deviceAuthorization") {
|
||||||
page(name: "Credentials", title: "Connect to your Logitech Harmony device", content: "authPage", install: false, nextPage: "deviceAuthorization")
|
page(name: "Credentials", title: "Connect to your Logitech Harmony device", content: "authPage", install: false, nextPage: "deviceAuthorization")
|
||||||
page(name: "deviceAuthorization", title: "Logitech Harmony device authorization", install: true) {
|
page(name: "deviceAuthorization", title: "Logitech Harmony device authorization", install: true) {
|
||||||
section("Allow Logitech Harmony to control these things...") {
|
section("Allow Logitech Harmony to control these things...") {
|
||||||
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
||||||
@@ -102,8 +102,7 @@ def authPage() {
|
|||||||
description = "Click to enter Harmony Credentials"
|
description = "Click to enter Harmony Credentials"
|
||||||
def redirectUrl = buildRedirectUrl
|
def redirectUrl = buildRedirectUrl
|
||||||
return dynamicPage(name: "Credentials", title: "Harmony", nextPage: null, uninstall: true, install:false) {
|
return dynamicPage(name: "Credentials", title: "Harmony", nextPage: null, uninstall: true, install:false) {
|
||||||
section { paragraph title: "Note:", "This device has not been officially tested and certified to “Work with SmartThings”. You can connect it to your SmartThings home but performance may vary and we will not be able to provide support or assistance." }
|
section { href url:redirectUrl, style:"embedded", required:true, title:"Harmony", description:description }
|
||||||
section { href url:redirectUrl, style:"embedded", required:true, title:"Harmony", description:description }
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//device discovery request every 5 //25 seconds
|
//device discovery request every 5 //25 seconds
|
||||||
@@ -315,6 +314,8 @@ def installed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
|
unsubscribe()
|
||||||
|
unschedule()
|
||||||
if (!state.accessToken) {
|
if (!state.accessToken) {
|
||||||
log.debug "About to create access token"
|
log.debug "About to create access token"
|
||||||
createAccessToken()
|
createAccessToken()
|
||||||
|
|||||||
@@ -111,23 +111,16 @@ private sendMessage(evt) {
|
|||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts(msg, recipients, options)
|
sendNotificationToContacts(msg, recipients, options)
|
||||||
} else {
|
} else {
|
||||||
|
if (pushAndPhone != 'No') {
|
||||||
|
log.debug 'sending push'
|
||||||
|
options.method = 'push'
|
||||||
|
sendNotification(msg, options)
|
||||||
|
}
|
||||||
if (phone) {
|
if (phone) {
|
||||||
options.phone = phone
|
options.phone = phone
|
||||||
if (pushAndPhone != 'No') {
|
log.debug 'sending SMS'
|
||||||
log.debug 'Sending push and SMS'
|
sendNotification(msg, options)
|
||||||
options.method = 'both'
|
|
||||||
} else {
|
|
||||||
log.debug 'Sending SMS'
|
|
||||||
options.method = 'phone'
|
|
||||||
}
|
|
||||||
} else if (pushAndPhone != 'No') {
|
|
||||||
log.debug 'Sending push'
|
|
||||||
options.method = 'push'
|
|
||||||
} else {
|
|
||||||
log.debug 'Sending nothing'
|
|
||||||
options.method = 'none'
|
|
||||||
}
|
}
|
||||||
sendNotification(msg, options)
|
|
||||||
}
|
}
|
||||||
if (frequency) {
|
if (frequency) {
|
||||||
state[evt.deviceId] = now()
|
state[evt.deviceId] = now()
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ def updated() {
|
|||||||
|
|
||||||
def presenceHandler(evt) {
|
def presenceHandler(evt) {
|
||||||
if (evt.value == "present") {
|
if (evt.value == "present") {
|
||||||
// log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
|
log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
|
||||||
sendPush("${presence.label ?: presence.name} has arrived at the ${location}")
|
sendPush("${presence.label ?: presence.name} has arrived at the ${location}")
|
||||||
} else if (evt.value == "not present") {
|
} else if (evt.value == "not present") {
|
||||||
// log.debug "${presence.label ?: presence.name} has left the ${location}"
|
log.debug "${presence.label ?: presence.name} has left the ${location}"
|
||||||
sendPush("${presence.label ?: presence.name} has left the ${location}")
|
sendPush("${presence.label ?: presence.name} has left the ${location}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def updated() {
|
|||||||
|
|
||||||
def presenceHandler(evt) {
|
def presenceHandler(evt) {
|
||||||
if (evt.value == "present") {
|
if (evt.value == "present") {
|
||||||
// log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
|
log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
|
||||||
|
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("${presence.label ?: presence.name} has arrived at the ${location}", recipients)
|
sendNotificationToContacts("${presence.label ?: presence.name} has arrived at the ${location}", recipients)
|
||||||
@@ -56,7 +56,7 @@ def presenceHandler(evt) {
|
|||||||
sendSms(phone1, "${presence.label ?: presence.name} has arrived at the ${location}")
|
sendSms(phone1, "${presence.label ?: presence.name} has arrived at the ${location}")
|
||||||
}
|
}
|
||||||
} else if (evt.value == "not present") {
|
} else if (evt.value == "not present") {
|
||||||
// log.debug "${presence.label ?: presence.name} has left the ${location}"
|
log.debug "${presence.label ?: presence.name} has left the ${location}"
|
||||||
|
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("${presence.label ?: presence.name} has left the ${location}", recipients)
|
sendNotificationToContacts("${presence.label ?: presence.name} has left the ${location}", recipients)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ def updated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def subscribe() {
|
def subscribe() {
|
||||||
// log.debug "present: ${cars.collect{it.displayName + ': ' + it.currentPresence}}"
|
log.debug "present: ${cars.collect{it.displayName + ': ' + it.currentPresence}}"
|
||||||
subscribe(doorSensor, "contact", garageDoorContact)
|
subscribe(doorSensor, "contact", garageDoorContact)
|
||||||
|
|
||||||
subscribe(cars, "presence", carPresence)
|
subscribe(cars, "presence", carPresence)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def updated() {
|
|||||||
private subscribeToEvents()
|
private subscribeToEvents()
|
||||||
{
|
{
|
||||||
subscribe intrusionMotions, "motion", intruderMotion
|
subscribe intrusionMotions, "motion", intruderMotion
|
||||||
// subscribe residentMotions, "motion", residentMotion
|
subscribe residentMotions, "motion", residentMotion
|
||||||
subscribe intrusionContacts, "contact", contact
|
subscribe intrusionContacts, "contact", contact
|
||||||
subscribe alarms, "alarm", alarm
|
subscribe alarms, "alarm", alarm
|
||||||
subscribe(app, appTouch)
|
subscribe(app, appTouch)
|
||||||
@@ -156,7 +156,6 @@ def residentMotion(evt)
|
|||||||
// startReArmSequence()
|
// startReArmSequence()
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
unsubscribe(residentMotions)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def contact(evt)
|
def contact(evt)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ def updated()
|
|||||||
|
|
||||||
def contactOpenHandler(evt) {
|
def contactOpenHandler(evt) {
|
||||||
log.trace "$evt.value: $evt, $settings"
|
log.trace "$evt.value: $evt, $settings"
|
||||||
log.debug "$contact1 was opened, sending text"
|
log.debug "$contact1 was opened, texting $phone1"
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("Your ${contact1.label ?: contact1.name} was opened", recipients)
|
sendNotificationToContacts("Your ${contact1.label ?: contact1.name} was opened", recipients)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,14 +60,14 @@ def motionActiveHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
if (alreadySentSms) {
|
||||||
log.debug "SMS already sent within the last $deltaSeconds seconds"
|
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
|
||||||
} else {
|
} else {
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
log.debug "$motion1 has moved while you were out, sending notifications to: ${recipients?.size()}"
|
log.debug "$motion1 has moved while you were out, sending notifications to: ${recipients?.size()}"
|
||||||
sendNotificationToContacts("${motion1.label} ${motion1.name} moved while you were out", recipients)
|
sendNotificationToContacts("${motion1.label} ${motion1.name} moved while you were out", recipients)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug "$motion1 has moved while you were out, sending text"
|
log.debug "$motion1 has moved while you were out, texting $phone1"
|
||||||
sendSms(phone1, "${motion1.label} ${motion1.name} moved while you were out")
|
sendSms(phone1, "${motion1.label} ${motion1.name} moved while you were out")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,13 +53,13 @@ def accelerationActiveHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
if (alreadySentSms) {
|
||||||
log.debug "SMS already sent to phone within the last $deltaSeconds seconds"
|
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
|
||||||
} else {
|
} else {
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("Gun case has moved!", recipients)
|
sendNotificationToContacts("Gun case has moved!", recipients)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug "$accelerationSensor has moved, texting phone"
|
log.debug "$accelerationSensor has moved, texting $phone1"
|
||||||
sendSms(phone1, "Gun case has moved!")
|
sendSms(phone1, "Gun case has moved!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ def firstPage()
|
|||||||
def lightSwitchesDiscovered = lightSwitchesDiscovered()
|
def lightSwitchesDiscovered = lightSwitchesDiscovered()
|
||||||
|
|
||||||
return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: true) {
|
return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: true) {
|
||||||
section { paragraph title: "Note:", "This device has not been officially tested and certified to “Work with SmartThings”. You can connect it to your SmartThings home but performance may vary and we will not be able to provide support or assistance." }
|
|
||||||
section("Select a device...") {
|
section("Select a device...") {
|
||||||
input "selectedSwitches", "enum", required:false, title:"Select Wemo Switches \n(${switchesDiscovered.size() ?: 0} found)", multiple:true, options:switchesDiscovered
|
input "selectedSwitches", "enum", required:false, title:"Select Wemo Switches \n(${switchesDiscovered.size() ?: 0} found)", multiple:true, options:switchesDiscovered
|
||||||
input "selectedMotions", "enum", required:false, title:"Select Wemo Motions \n(${motionsDiscovered.size() ?: 0} found)", multiple:true, options:motionsDiscovered
|
input "selectedMotions", "enum", required:false, title:"Select Wemo Motions \n(${motionsDiscovered.size() ?: 0} found)", multiple:true, options:motionsDiscovered
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def authPage() {
|
|||||||
|
|
||||||
def oauthInitUrl() {
|
def oauthInitUrl() {
|
||||||
def token = getToken()
|
def token = getToken()
|
||||||
//log.debug "initiateOauth got token: $token"
|
log.debug "initiateOauth got token: $token"
|
||||||
|
|
||||||
// store these for validate after the user takes the oauth journey
|
// store these for validate after the user takes the oauth journey
|
||||||
state.oauth_request_token = token.oauth_token
|
state.oauth_request_token = token.oauth_token
|
||||||
@@ -76,7 +76,7 @@ def getToken() {
|
|||||||
]
|
]
|
||||||
def requestTokenBaseUrl = "https://oauth.withings.com/account/request_token"
|
def requestTokenBaseUrl = "https://oauth.withings.com/account/request_token"
|
||||||
def url = buildSignedUrl(requestTokenBaseUrl, params)
|
def url = buildSignedUrl(requestTokenBaseUrl, params)
|
||||||
//log.debug "getToken - url: $url"
|
log.debug "getToken - url: $url"
|
||||||
|
|
||||||
return getJsonFromUrl(url)
|
return getJsonFromUrl(url)
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ def exchangeToken() {
|
|||||||
|
|
||||||
def requestTokenBaseUrl = "https://oauth.withings.com/account/access_token"
|
def requestTokenBaseUrl = "https://oauth.withings.com/account/access_token"
|
||||||
def url = buildSignedUrl(requestTokenBaseUrl, params, tokenSecret)
|
def url = buildSignedUrl(requestTokenBaseUrl, params, tokenSecret)
|
||||||
//log.debug "signed url: $url with secret $tokenSecret"
|
log.debug "signed url: $url with secret $tokenSecret"
|
||||||
|
|
||||||
def token = getJsonFromUrl(url)
|
def token = getJsonFromUrl(url)
|
||||||
|
|
||||||
@@ -198,8 +198,8 @@ def exchangeToken() {
|
|||||||
|
|
||||||
def load() {
|
def load() {
|
||||||
def json = get(getMeasurement(new Date() - 30))
|
def json = get(getMeasurement(new Date() - 30))
|
||||||
// removed logging of actual json payload. Can be put back for debugging
|
|
||||||
log.debug "swapped, then received json"
|
log.debug "swapped, then received: $json"
|
||||||
parse(data:json)
|
parse(data:json)
|
||||||
|
|
||||||
def html = """
|
def html = """
|
||||||
|
|||||||
Reference in New Issue
Block a user