mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 21:03:00 +00:00
Compare commits
109 Commits
PROD_2016.
...
MSA-1474-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
309439a391 | ||
|
|
a6c7ab49b6 | ||
|
|
629d768575 | ||
|
|
4523498dab | ||
|
|
e89e45e000 | ||
|
|
78ec280e83 | ||
|
|
1f144d36e4 | ||
|
|
f5bd580c9e | ||
|
|
d5329dbde3 | ||
|
|
48818cfb06 | ||
|
|
079919260b | ||
|
|
570922e2ac | ||
|
|
53ed9b4d2b | ||
|
|
7149a81c85 | ||
|
|
30274f0cd7 | ||
|
|
8869cd3af0 | ||
|
|
fb0caa6446 | ||
|
|
3d05d42cb8 | ||
|
|
3184615e87 | ||
|
|
0f70362e0a | ||
|
|
bc817f8530 | ||
|
|
01b8399893 | ||
|
|
81318bafac | ||
|
|
60c2006bfc | ||
|
|
1e4f1223e7 | ||
|
|
b78bce55b2 | ||
|
|
01593c3973 | ||
|
|
6862785d6c | ||
|
|
763d7411e2 | ||
|
|
c703543f36 | ||
|
|
5db6ecda3e | ||
|
|
43b836f413 | ||
|
|
006b5e7bea | ||
|
|
62c8c19805 | ||
|
|
48e9a4bd6a | ||
|
|
07a4c0decc | ||
|
|
6aa09bb052 | ||
|
|
2f889de11a | ||
|
|
5584020e96 | ||
|
|
4ef2e694c2 | ||
|
|
826993cc45 | ||
|
|
ae3306928b | ||
|
|
8777ec5f6d | ||
|
|
91eb59a10d | ||
|
|
324ac13afb | ||
|
|
878eb66b8b | ||
|
|
8dfc270c2d | ||
|
|
614573a15c | ||
|
|
9c7b0875ba | ||
|
|
7568cbf781 | ||
|
|
1858c280a5 | ||
|
|
6b7d0968f6 | ||
|
|
159d3acf4f | ||
|
|
e8101630a3 | ||
|
|
f5ba78b221 | ||
|
|
19b8a7eeb9 | ||
|
|
1b37d649a5 | ||
|
|
dedb0f8465 | ||
|
|
06acc13575 | ||
|
|
c051d719cc | ||
|
|
1e54b93b0c | ||
|
|
bac37f9ca2 | ||
|
|
f0ecb65c09 | ||
|
|
1c0ddd2571 | ||
|
|
b7e0cbda09 | ||
|
|
f80e094bd9 | ||
|
|
ce9ac624d0 | ||
|
|
f3f5cc42c9 | ||
|
|
313fe8b734 | ||
|
|
0d693386d1 | ||
|
|
d1aee1e874 | ||
|
|
3528e7da51 | ||
|
|
5c2e06c98d | ||
|
|
26df619b4f | ||
|
|
af2ea04442 | ||
|
|
383f72580a | ||
|
|
090a306939 | ||
|
|
d0a16c10b2 | ||
|
|
faa65f204d | ||
|
|
bacd335991 | ||
|
|
740e5e096c | ||
|
|
aac2f9b177 | ||
|
|
048eb77e64 | ||
|
|
dadec937fa | ||
|
|
78aa6691c4 | ||
|
|
315918dc6f | ||
|
|
5e6b4f74e0 | ||
|
|
276f7d3b43 | ||
|
|
ce12ad5013 | ||
|
|
beed783d19 | ||
|
|
7f347638d5 | ||
|
|
1f8ce734e7 | ||
|
|
17bf040c7e | ||
|
|
f1c3f5942b | ||
|
|
212c9c4179 | ||
|
|
4898006e4e | ||
|
|
d3eb7f756f | ||
|
|
b95ba37364 | ||
|
|
87f8755faf | ||
|
|
555a9f5ab4 | ||
|
|
0744384dbf | ||
|
|
97e0e9d0f8 | ||
|
|
655e756b1b | ||
|
|
7ce7ad86bd | ||
|
|
24c64608a9 | ||
|
|
dbc2a1e45c | ||
|
|
92cc8afdf7 | ||
|
|
e545842f7c | ||
|
|
22185c5440 |
30
build.gradle
30
build.gradle
@@ -19,7 +19,7 @@ buildscript {
|
||||
username smartThingsArtifactoryUserName
|
||||
password smartThingsArtifactoryPassword
|
||||
}
|
||||
url "http://artifactory.smartthings.com/libs-release-local"
|
||||
url "https://artifactory.smartthings.com/libs-release-local"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,37 @@ buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
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 {
|
||||
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 {
|
||||
|
||||
@@ -5,7 +5,9 @@ machine:
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- echo "Nothing to do."
|
||||
- ./gradlew dependencies -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||
post:
|
||||
- ./gradlew compileSmartappsGroovy compileDevicetypesGroovy -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||
|
||||
test:
|
||||
override:
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") {
|
||||
capability "Sensor"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Temperature Measurement"
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") {
|
||||
capability "Sensor"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Temperature Measurement"
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") {
|
||||
capability "Sensor"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Temperature Measurement"
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") {
|
||||
capability "Sensor"
|
||||
|
||||
attribute "rain", "number"
|
||||
attribute "rainSumHour", "number"
|
||||
attribute "rainSumDay", "number"
|
||||
|
||||
@@ -33,8 +33,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
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") {
|
||||
attributeState("inactive", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
||||
attributeState("active", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
||||
attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
||||
attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
@@ -278,4 +278,4 @@ private encap(physicalgraph.zwave.Command cmd) {
|
||||
} else {
|
||||
crc16(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +274,7 @@ private Map makeTemperatureResult(value) {
|
||||
name: 'temperature',
|
||||
value: "" + value,
|
||||
descriptionText: "${linkText} is ${value}°${temperatureScale}",
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -254,7 +254,8 @@ private Map getTemperatureResult(value) {
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText
|
||||
descriptionText: descriptionText,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
//metadata for smartapps
|
||||
metadata {
|
||||
definition (name: "Danfoss LC13 TRV V0.7", namespace: "richdp", author: "RichardP") {
|
||||
capability "Actuator"
|
||||
capability "Temperature Measurement"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Thermostat"
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
capability "Sensor"
|
||||
|
||||
attribute "thermostatFanState", "string"
|
||||
|
||||
command "switchMode"
|
||||
command "switchFanMode"
|
||||
command "quickSetCool"
|
||||
command "quickSetHeat"
|
||||
|
||||
fingerprint deviceId: "0x0804"
|
||||
fingerprint inClusters: "0x80, 0x46, 0x81, 0x72, 0x8F, 0x75, 0x43, 0x86, 0x84"
|
||||
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
status "off" : "command: 4003, payload: 00"
|
||||
status "heat" : "command: 4003, payload: 01"
|
||||
status "cool" : "command: 4003, payload: 02"
|
||||
status "auto" : "command: 4003, payload: 03"
|
||||
status "emergencyHeat" : "command: 4003, payload: 04"
|
||||
|
||||
status "fanAuto" : "command: 4403, payload: 00"
|
||||
status "fanOn" : "command: 4403, payload: 01"
|
||||
status "fanCirculate" : "command: 4403, payload: 06"
|
||||
|
||||
status "heat 60" : "command: 4303, payload: 01 09 3C"
|
||||
status "heat 68" : "command: 4303, payload: 01 09 44"
|
||||
status "heat 72" : "command: 4303, payload: 01 09 48"
|
||||
|
||||
status "cool 72" : "command: 4303, payload: 02 09 48"
|
||||
status "cool 76" : "command: 4303, payload: 02 09 4C"
|
||||
status "cool 80" : "command: 4303, payload: 02 09 50"
|
||||
|
||||
status "temp 58" : "command: 3105, payload: 01 2A 02 44"
|
||||
status "temp 62" : "command: 3105, payload: 01 2A 02 6C"
|
||||
status "temp 70" : "command: 3105, payload: 01 2A 02 BC"
|
||||
status "temp 74" : "command: 3105, payload: 01 2A 02 E4"
|
||||
status "temp 78" : "command: 3105, payload: 01 2A 03 0C"
|
||||
status "temp 82" : "command: 3105, payload: 01 2A 03 34"
|
||||
|
||||
status "idle" : "command: 4203, payload: 00"
|
||||
status "heating" : "command: 4203, payload: 01"
|
||||
status "cooling" : "command: 4203, payload: 02"
|
||||
status "fan only" : "command: 4203, payload: 03"
|
||||
status "pending heat" : "command: 4203, payload: 04"
|
||||
status "pending cool" : "command: 4203, payload: 05"
|
||||
status "vent economizer": "command: 4203, payload: 06"
|
||||
|
||||
// reply messages
|
||||
reply "2502": "command: 2503, payload: FF"
|
||||
}
|
||||
|
||||
//UI configuration
|
||||
tiles {
|
||||
// Main Tile displays current set point recieved from thermostat.
|
||||
// Background color changes with temp.
|
||||
valueTile("temperature", "device.currentHeatingSetpoint", width: 3, height: 2) {
|
||||
state("device.currentHeatingSetpoint", label:'${currentValue}°',
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#ededed"],
|
||||
[value: 4, color: "#153591"], //blue
|
||||
[value: 16, color: "#178998"],
|
||||
[value: 18, color: "#199f5c"],
|
||||
[value: 20, color: "#2da71c"],//green
|
||||
[value: 21, color: "#5baa1d"],
|
||||
[value: 22, color: "#8aae1e"],
|
||||
[value: 23, color: "#b1a81f"],
|
||||
[value: 24, color: "#b57d20"],
|
||||
[value: 26, color: "#b85122"],
|
||||
[value: 28, color: "#bc2323"] //red
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
// Slider control for controling the set point which is next sent to the thermostat
|
||||
controlTile("heatSliderControl", "device.nextHeatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false, range:"(4..28)" ) {
|
||||
state "setHeatingSetpoint", action: "quickSetHeat", backgroundColor:"#d04e00"
|
||||
}
|
||||
|
||||
// Tile to indicate the set point that will next be sent to the thermostat. Controlled by controlTile above.
|
||||
valueTile("nextHeatingSetpoint", "device.nextHeatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "heat", label:'${currentValue}° next', backgroundColor:"#ffffff"
|
||||
}
|
||||
|
||||
// Display battery %
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 1, height: 1) {
|
||||
tileAttribute ("device.battery", key: "PRIMARY_CONTROL"){
|
||||
state "battery", label:'${currentValue}% battery', unit:""}
|
||||
}
|
||||
|
||||
// Standard Refresh Tile
|
||||
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat", width: 1, height: 1) {
|
||||
state "default", action:"polling.poll", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
// Standard Config Tile
|
||||
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 1, height: 1) {
|
||||
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
||||
}
|
||||
|
||||
// Order of Tiles
|
||||
main "temperature"
|
||||
details(["temperature", "heatSliderControl", "nextHeatingSetpoint", "battery", "refresh", "configure"])
|
||||
}
|
||||
} //end of UI config
|
||||
|
||||
//Parse string to recieve information from device
|
||||
def parse(String description) {
|
||||
state.count = 0
|
||||
def results = []
|
||||
log.debug("RAW command: $description")
|
||||
if (description.startsWith("Err")) {
|
||||
log.debug("An error has occurred")
|
||||
}
|
||||
else {
|
||||
def cmd = zwave.parse(description,[0x80: 1, 0x46: 1, 0x81: 1, 0x72: 2, 0x8F: 1, 0x75: 2, 0x43: 2, 0x86: 1, 0x84: 2])
|
||||
log.debug "Parsed Command: $cmd"
|
||||
if (cmd) {
|
||||
results = zwaveEvent(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event Generation
|
||||
// Event - Get Battery Report
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [ name: "battery", unit: "%" ]
|
||||
if (cmd.batteryLevel == 0xFF) { // Special value for low battery alert
|
||||
map.value = 1
|
||||
map.descriptionText = "${device.displayName} has a low battery"
|
||||
map.isStateChange = true
|
||||
} else {
|
||||
map.value = cmd.batteryLevel
|
||||
log.debug ("Battery: $cmd.batteryLevel")
|
||||
}
|
||||
// Store time of last battery update so we don't ask every wakeup, see WakeUpNotification handler
|
||||
state.lastbatt = new Date().time
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
// Event - Get Override Schedule
|
||||
def zwaveEvent(physicalgraph.zwave.commands.climatecontrolschedulev1.ScheduleOverrideReport cmd){
|
||||
log.debug "--- Schedule Override Report received: $cmd"
|
||||
}
|
||||
|
||||
// Event - Thermostat Report
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd) {
|
||||
log.debug "--- Thermostat Report recieved: $cmd"
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
def map = [:]
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.displayed = false
|
||||
map.name = "currentHeatingSetpoint"
|
||||
log.debug ("Parsed Thermostat Set Point: $map")
|
||||
|
||||
// So we can respond with same format
|
||||
state.size = cmd.size
|
||||
state.scale = cmd.scale
|
||||
state.precision = cmd.precision
|
||||
map
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
// Event for waking up the battery powered device
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
|
||||
def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: true)]
|
||||
|
||||
// Only ask for battery if we haven't had a BatteryReport in a while
|
||||
if (!state.lastbatt || (new Date().time) - state.lastbatt > 24*60*60*1000) {
|
||||
result << response(zwave.batteryV1.batteryGet())
|
||||
result << response("delay 1200") // leave time for device to respond to batteryGet
|
||||
}
|
||||
//Send new set point to TRV on wake up
|
||||
result << response (configureSetPoint())
|
||||
result << response ("delay 2000")
|
||||
|
||||
//Confirm current set point and update UI
|
||||
result << response (updateUI())
|
||||
result << response ("delay 2000")
|
||||
|
||||
// wait and then send no more information confirmation
|
||||
result << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
log.debug "Zwave event received: $cmd"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
log.warn "Unexpected zwave command $cmd"
|
||||
}
|
||||
|
||||
// Command Implementations
|
||||
def poll() {
|
||||
delayBetween([
|
||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(), //chaged to V1 for testing
|
||||
], standardDelay)
|
||||
}
|
||||
|
||||
def quickSetHeat(degrees) {
|
||||
setHeatingSetpoint(degrees, 1000)
|
||||
}
|
||||
|
||||
def nextHeatingSetpoint(degrees, delay = 3600) {
|
||||
setHeatingSetpoint(degrees.toDouble(), delay)
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(Double degrees, Integer delay = 3600) {
|
||||
log.trace "setNextHeatingSetpoint($degrees, $delay)"
|
||||
def deviceScale = state.scale ?: 2
|
||||
def deviceScaleString = deviceScale == 2 ? "C" : "F"
|
||||
def locationScale = getTemperatureScale()
|
||||
def p = (state.precision == null) ? 1 : state.precision
|
||||
|
||||
def convertedDegrees
|
||||
if (locationScale == "C" && deviceScaleString == "F") {
|
||||
convertedDegrees = celsiusToFahrenheit(degrees)
|
||||
} else if (locationScale == "F" && deviceScaleString == "C") {
|
||||
convertedDegrees = fahrenheitToCelsius(degrees)
|
||||
} else {
|
||||
convertedDegrees = degrees
|
||||
}
|
||||
|
||||
sendEvent(name:"nextHeatingSetpoint", value:convertedDegrees, isStateChange: true)
|
||||
log.debug ("Value to be sent to thermostat on next wakeup: $convertedDegrees")
|
||||
state.nextHeatingSetpoint = convertedDegrees
|
||||
}
|
||||
|
||||
|
||||
//send new set point to thermostat
|
||||
def configureSetPoint(){
|
||||
log.debug "*** sending new setpoint"
|
||||
def sendHeatingSetpoint = state.nextHeatingSetpoint
|
||||
delayBetween([
|
||||
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: 0, precision: 2, scaledValue: sendHeatingSetpoint).format(),
|
||||
], 2000)
|
||||
}
|
||||
|
||||
//get the current setpoint and update the UI
|
||||
def updateUI(){
|
||||
delayBetween([
|
||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
|
||||
], 2000)
|
||||
}
|
||||
|
||||
//configuration
|
||||
def configure() {
|
||||
delayBetween([
|
||||
zwave.configurationV1.configurationSet(parameterNumber:1, size:2, scaledConfigurationValue:100).format(),
|
||||
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
|
||||
zwave.wakeUpV1.wakeUpIntervalSet(seconds:3600, nodeid:zwaveHubNodeId).format()
|
||||
], standardDelay)
|
||||
}
|
||||
|
||||
//standardDelay
|
||||
private getStandardDelay() {
|
||||
1000
|
||||
}
|
||||
@@ -47,6 +47,9 @@ metadata {
|
||||
|
||||
command "everywhereJoin"
|
||||
command "everywhereLeave"
|
||||
|
||||
command "forceOff"
|
||||
command "forceOn"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,9 +67,9 @@ metadata {
|
||||
}
|
||||
|
||||
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.Electronics.electronics16", backgroundColor: "#79b821", nextState:"turningOff"
|
||||
state "on", label: '${name}', action: "forceOff", icon: "st.Electronics.electronics16", backgroundColor: "#79b821", nextState:"turningOff"
|
||||
state "turningOff", label:'TURNING OFF', icon:"st.Electronics.electronics16", backgroundColor:"#ffffff"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff", nextState:"turningOn"
|
||||
state "off", label: '${name}', action: "forceOn", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'TURNING ON', icon:"st.Electronics.electronics16", backgroundColor:"#79b821"
|
||||
}
|
||||
valueTile("1", "device.station1", decoration: "flat", canChangeIcon: false) {
|
||||
@@ -140,8 +143,22 @@ metadata {
|
||||
* one place.
|
||||
*
|
||||
*/
|
||||
def off() { onAction("off") }
|
||||
def on() { onAction("on") }
|
||||
def off() {
|
||||
if (device.currentState("switch")?.value == "on") {
|
||||
onAction("off")
|
||||
}
|
||||
}
|
||||
def forceOff() {
|
||||
onAction("off")
|
||||
}
|
||||
def on() {
|
||||
if (device.currentState("switch")?.value == "off") {
|
||||
onAction("on")
|
||||
}
|
||||
}
|
||||
def forceOn() {
|
||||
onAction("on")
|
||||
}
|
||||
def volup() { onAction("volup") }
|
||||
def voldown() { onAction("voldown") }
|
||||
def preset1() { onAction("1") }
|
||||
@@ -240,11 +257,11 @@ def onAction(String user, data=null) {
|
||||
def actions = null
|
||||
switch (user) {
|
||||
case "on":
|
||||
actions = boseSetPowerState(true)
|
||||
boseSetPowerState(true)
|
||||
break
|
||||
case "off":
|
||||
boseSetNowPlaying(null, "STANDBY")
|
||||
actions = boseSetPowerState(false)
|
||||
boseSetPowerState(false)
|
||||
break
|
||||
case "volume":
|
||||
actions = boseSetVolume(data)
|
||||
|
||||
@@ -89,14 +89,17 @@ def parse(String description) {
|
||||
log.debug "TEMP"
|
||||
map.name = "temperature"
|
||||
map.value = getTemperature(descMap.value)
|
||||
map.unit = temperatureScale
|
||||
} else if (descMap.cluster == "0201" && descMap.attrId == "0011") {
|
||||
log.debug "COOLING SETPOINT"
|
||||
map.name = "coolingSetpoint"
|
||||
map.value = getTemperature(descMap.value)
|
||||
map.unit = temperatureScale
|
||||
} else if (descMap.cluster == "0201" && descMap.attrId == "0012") {
|
||||
log.debug "HEATING SETPOINT"
|
||||
map.name = "heatingSetpoint"
|
||||
map.value = getTemperature(descMap.value)
|
||||
map.unit = temperatureScale
|
||||
} else if (descMap.cluster == "0201" && descMap.attrId == "001c") {
|
||||
log.debug "MODE"
|
||||
map.name = "thermostatMode"
|
||||
@@ -169,7 +172,7 @@ def setHeatingSetpoint(degrees) {
|
||||
|
||||
def degreesInteger = Math.round(degrees)
|
||||
log.debug "setHeatingSetpoint({$degreesInteger} ${temperatureScale})"
|
||||
sendEvent("name": "heatingSetpoint", "value": degreesInteger)
|
||||
sendEvent("name": "heatingSetpoint", "value": degreesInteger, "unit": temperatureScale)
|
||||
|
||||
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
|
||||
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius * 100) + "}"
|
||||
@@ -180,7 +183,7 @@ def setCoolingSetpoint(degrees) {
|
||||
if (degrees != null) {
|
||||
def degreesInteger = Math.round(degrees)
|
||||
log.debug "setCoolingSetpoint({$degreesInteger} ${temperatureScale})"
|
||||
sendEvent("name": "coolingSetpoint", "value": degreesInteger)
|
||||
sendEvent("name": "coolingSetpoint", "value": degreesInteger, "unit": temperatureScale)
|
||||
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
|
||||
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius * 100) + "}"
|
||||
}
|
||||
|
||||
2
devicetypes/smartthings/cree-bulb.src/.st-ignore
Normal file
2
devicetypes/smartthings/cree-bulb.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
36
devicetypes/smartthings/cree-bulb.src/README.md
Normal file
36
devicetypes/smartthings/cree-bulb.src/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Connected Cree LED Bulb
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Connected Cree LED Bulb](https://support.smartthings.com/hc/en-us/articles/204258280-Cree-Connected-LED-Bulb)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Switch Level** - represents current light level, usually 0-100 in percent
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C6 Connected Cree LED Bulb with maxReportTime of 10 min.
|
||||
Check-in interval is double the value of maxReportTime for Zigbee device.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 2*10 = 20 min
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Cree Connected LED Bulb Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204258280-Cree-Connected-LED-Bulb)
|
||||
@@ -67,12 +67,6 @@ def parse(String description) {
|
||||
def resultMap = zigbee.getEvent(description)
|
||||
if (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 {
|
||||
log.debug "DID NOT PARSE MESSAGE for description : $description"
|
||||
@@ -96,15 +90,7 @@ def setLevel(value) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -117,6 +103,8 @@ def poll() {
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
// minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
@@ -67,6 +67,6 @@ def refresh() {
|
||||
|
||||
void poll() {
|
||||
log.debug "Executing 'poll' using parent SmartApp"
|
||||
parent.pollChild()
|
||||
parent.poll()
|
||||
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ def refresh() {
|
||||
|
||||
void poll() {
|
||||
log.debug "Executing 'poll' using parent SmartApp"
|
||||
parent.pollChild()
|
||||
parent.poll()
|
||||
}
|
||||
|
||||
def generateEvent(Map results) {
|
||||
@@ -152,11 +152,11 @@ def generateEvent(Map results) {
|
||||
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||
isChange = isTemperatureStateChange(device, name, value.toString())
|
||||
isDisplayed = isChange
|
||||
event << [value: sendValue, isStateChange: isChange, displayed: isDisplayed]
|
||||
event << [value: sendValue, unit: temperatureScale, isStateChange: isChange, displayed: isDisplayed]
|
||||
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
|
||||
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||
event << [value: sendValue, displayed: false]
|
||||
event << [value: sendValue, unit: temperatureScale, displayed: false]
|
||||
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
event << [value: value.toString(), isStateChange: isChange, displayed: false]
|
||||
@@ -234,9 +234,9 @@ void setHeatingSetpoint(setpoint) {
|
||||
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||
|
||||
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
||||
if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
|
||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
|
||||
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
|
||||
log.debug "Done setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
|
||||
generateSetpointEvent()
|
||||
generateStatusEvent()
|
||||
@@ -271,9 +271,9 @@ void setCoolingSetpoint(setpoint) {
|
||||
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||
|
||||
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
||||
if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
|
||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
|
||||
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
|
||||
log.debug "Done setCoolingSetpoint>> coolingSetpoint = ${coolingSetpoint}, heatingSetpoint = ${heatingSetpoint}"
|
||||
generateSetpointEvent()
|
||||
generateStatusEvent()
|
||||
@@ -287,14 +287,14 @@ void resumeProgram() {
|
||||
log.debug "resumeProgram() is called"
|
||||
sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false)
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.resumeProgram(this, deviceId)) {
|
||||
if (parent.resumeProgram(deviceId)) {
|
||||
sendEvent("name":"thermostatStatus", "value":"setpoint is updating", "description":statusText, displayed: false)
|
||||
runIn(5, "poll")
|
||||
log.debug "resumeProgram() is done"
|
||||
sendEvent("name":"resumeProgram", "value":"resume", descriptionText: "resumeProgram is done", displayed: false, isStateChange: true)
|
||||
} else {
|
||||
sendEvent("name":"thermostatStatus", "value":"failed resume click refresh", "description":statusText, displayed: false)
|
||||
log.error "Error resumeProgram() check parent.resumeProgram(this, deviceId)"
|
||||
log.error "Error resumeProgram() check parent.resumeProgram(deviceId)"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -406,7 +406,7 @@ def generateOperatingStateEvent(operatingState) {
|
||||
def off() {
|
||||
log.debug "off"
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.setMode (this,"off", deviceId))
|
||||
if (parent.setMode ("off", deviceId))
|
||||
generateModeEvent("off")
|
||||
else {
|
||||
log.debug "Error setting new mode."
|
||||
@@ -420,7 +420,7 @@ def off() {
|
||||
def heat() {
|
||||
log.debug "heat"
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.setMode (this,"heat", deviceId))
|
||||
if (parent.setMode ("heat", deviceId))
|
||||
generateModeEvent("heat")
|
||||
else {
|
||||
log.debug "Error setting new mode."
|
||||
@@ -438,7 +438,7 @@ def emergencyHeat() {
|
||||
def auxHeatOnly() {
|
||||
log.debug "auxHeatOnly"
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.setMode (this,"auxHeatOnly", deviceId))
|
||||
if (parent.setMode ("auxHeatOnly", deviceId))
|
||||
generateModeEvent("auxHeatOnly")
|
||||
else {
|
||||
log.debug "Error setting new mode."
|
||||
@@ -452,7 +452,7 @@ def auxHeatOnly() {
|
||||
def cool() {
|
||||
log.debug "cool"
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.setMode (this,"cool", deviceId))
|
||||
if (parent.setMode ("cool", deviceId))
|
||||
generateModeEvent("cool")
|
||||
else {
|
||||
log.debug "Error setting new mode."
|
||||
@@ -466,7 +466,7 @@ def cool() {
|
||||
def auto() {
|
||||
log.debug "auto"
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.setMode (this,"auto", deviceId))
|
||||
if (parent.setMode ("auto", deviceId))
|
||||
generateModeEvent("auto")
|
||||
else {
|
||||
log.debug "Error setting new mode."
|
||||
@@ -489,7 +489,7 @@ def fanOn() {
|
||||
def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint
|
||||
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||
|
||||
if (parent.setFanMode(this, heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) {
|
||||
if (parent.setFanMode(heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) {
|
||||
generateFanModeEvent(fanMode)
|
||||
} else {
|
||||
log.debug "Error setting new mode."
|
||||
@@ -510,7 +510,7 @@ def fanAuto() {
|
||||
def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint
|
||||
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||
|
||||
if (parent.setFanMode(this, heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) {
|
||||
if (parent.setFanMode(heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) {
|
||||
generateFanModeEvent(fanMode)
|
||||
} else {
|
||||
log.debug "Error setting new mode."
|
||||
@@ -556,12 +556,12 @@ def generateSetpointEvent() {
|
||||
|
||||
if (mode == "heat") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint )
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
}
|
||||
else if (mode == "cool") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint)
|
||||
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
} else if (mode == "auto") {
|
||||
|
||||
@@ -573,7 +573,7 @@ def generateSetpointEvent() {
|
||||
|
||||
} else if (mode == "auxHeatOnly") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint)
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
}
|
||||
|
||||
@@ -608,7 +608,7 @@ void raiseSetpoint() {
|
||||
targetvalue = maxCoolingSetpoint
|
||||
}
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
|
||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false)
|
||||
log.info "In mode $mode raiseSetpoint() to $targetvalue"
|
||||
|
||||
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
|
||||
@@ -644,7 +644,7 @@ void lowerSetpoint() {
|
||||
targetvalue = minCoolingSetpoint
|
||||
}
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
|
||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false)
|
||||
log.info "In mode $mode lowerSetpoint() to $targetvalue"
|
||||
|
||||
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
|
||||
@@ -690,10 +690,10 @@ void alterSetpoint(temp) {
|
||||
def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint
|
||||
def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint
|
||||
|
||||
if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||
sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false)
|
||||
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint)
|
||||
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint)
|
||||
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint, "unit": location.temperatureScale)
|
||||
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint, "unit": location.temperatureScale)
|
||||
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
|
||||
} else {
|
||||
log.error "Error alterSetpoint()"
|
||||
|
||||
@@ -682,7 +682,7 @@ def setHeatingSetpoint(degrees) {
|
||||
def temperatureScale = getTemperatureScale()
|
||||
|
||||
def degreesInteger = degrees as Integer
|
||||
sendEvent("name":"heatingSetpoint", "value":degreesInteger)
|
||||
sendEvent("name":"heatingSetpoint", "value":degreesInteger, "unit":temperatureScale)
|
||||
|
||||
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
|
||||
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius*100) + "}"
|
||||
@@ -691,7 +691,7 @@ def setHeatingSetpoint(degrees) {
|
||||
|
||||
def setCoolingSetpoint(degrees) {
|
||||
def degreesInteger = degrees as Integer
|
||||
sendEvent("name":"coolingSetpoint", "value":degreesInteger)
|
||||
sendEvent("name":"coolingSetpoint", "value":degreesInteger, "unit":temperatureScale)
|
||||
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
|
||||
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius*100) + "}"
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
command "setAdjustedColor"
|
||||
command "reset"
|
||||
@@ -55,6 +56,10 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(description) {
|
||||
log.debug "parse() - $description"
|
||||
@@ -166,3 +171,7 @@ def verifyPercent(percent) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.debug "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ def parse(description) {
|
||||
log.trace "HUE BRIDGE, GENERATING EVENT: $map.name: $map.value"
|
||||
results << createEvent(name: "${map.name}", value: "${map.value}")
|
||||
} else {
|
||||
log.trace "Parsing description"
|
||||
log.trace "Parsing description"
|
||||
def msg = parseLanMessage(description)
|
||||
if (msg.body) {
|
||||
def contentType = msg.headers["Content-Type"]
|
||||
@@ -72,13 +72,13 @@ def parse(description) {
|
||||
log.info "Bridge response: $msg.body"
|
||||
} else {
|
||||
// Sending Bulbs List to parent"
|
||||
if (parent.state.inBulbDiscovery)
|
||||
log.info parent.bulbListHandler(device.hub.id, msg.body)
|
||||
if (parent.isInBulbDiscovery())
|
||||
log.info parent.bulbListHandler(device.hub.id, msg.body)
|
||||
}
|
||||
}
|
||||
else if (contentType?.contains("xml")) {
|
||||
log.debug "HUE BRIDGE ALREADY PRESENT"
|
||||
parent.hubVerification(device.hub.id, msg.body)
|
||||
parent.hubVerification(device.hub.id, msg.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
command "setAdjustedColor"
|
||||
command "reset"
|
||||
@@ -64,6 +65,10 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(description) {
|
||||
log.debug "parse() - $description"
|
||||
@@ -182,3 +187,7 @@ def verifyPercent(percent) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.trace "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
command "refresh"
|
||||
}
|
||||
@@ -48,6 +49,10 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(description) {
|
||||
log.debug "parse() - $description"
|
||||
@@ -87,3 +92,7 @@ void refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
parent.manualRefresh()
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.debug "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ metadata {
|
||||
capability "Color Temperature"
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Health Check"
|
||||
|
||||
command "refresh"
|
||||
}
|
||||
@@ -53,6 +54,10 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(description) {
|
||||
log.debug "parse() - $description"
|
||||
@@ -101,3 +106,7 @@ void refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
parent.manualRefresh()
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.debug "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ def parse(String description) {
|
||||
|
||||
if (descMap.cluster == "0300") {
|
||||
if(descMap.attrId == "0000"){ //Hue Attribute
|
||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
|
||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
||||
log.debug "Hue value returned is $hueValue"
|
||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||
}
|
||||
@@ -203,7 +203,7 @@ def setLevel(value) {
|
||||
|
||||
//input Hue Integer values; returns color name for saturation 100%
|
||||
private getColorName(hueValue){
|
||||
if(hueValue>360 || hueValue<0)
|
||||
if(hueValue>100 || hueValue<0)
|
||||
return
|
||||
|
||||
hueValue = Math.round(hueValue / 100 * 360)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
Osram Flex RGBW Light Strip
|
||||
|
||||
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
metadata {
|
||||
definition (name: "OSRAM LIGHTIFY LED Flexible Strip RGBW", namespace: "smartthings", author: "SmartThings") {
|
||||
|
||||
|
||||
capability "Color Temperature"
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
@@ -18,7 +18,7 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Color Control"
|
||||
|
||||
|
||||
attribute "colorName", "string"
|
||||
|
||||
command "setAdjustedColor"
|
||||
@@ -49,7 +49,7 @@ metadata {
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
|
||||
state "colorTemperature", action:"color temperature.setColorTemperature"
|
||||
}
|
||||
@@ -118,7 +118,7 @@ def parse(String description) {
|
||||
}
|
||||
}
|
||||
else if(descMap.attrId == "0000"){ //Hue Attribute
|
||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
|
||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
||||
log.debug "Hue value returned is $hueValue"
|
||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||
}
|
||||
@@ -274,7 +274,7 @@ private getGenericName(value){
|
||||
|
||||
//input Hue Integer values; returns color name for saturation 100%
|
||||
private getColorName(hueValue){
|
||||
if(hueValue>360 || hueValue<0)
|
||||
if(hueValue>100 || hueValue<0)
|
||||
return
|
||||
|
||||
hueValue = Math.round(hueValue / 100 * 360)
|
||||
@@ -449,7 +449,7 @@ def setColor(value){
|
||||
def level = hex(value.level * 255 / 100)
|
||||
cmd << zigbeeSetLevel(level)
|
||||
}
|
||||
|
||||
|
||||
if (value.switch == "off") {
|
||||
cmd << "delay 150"
|
||||
cmd << off()
|
||||
|
||||
2
devicetypes/smartthings/smartpower-outlet.src/.st-ignore
Normal file
2
devicetypes/smartthings/smartpower-outlet.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
38
devicetypes/smartthings/smartpower-outlet.src/README.md
Normal file
38
devicetypes/smartthings/smartpower-outlet.src/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# SmartPower Outlet
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Samsung SmartPower Outlet](https://shop.smartthings.com/#!/products/smartpower-outlet)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Power Meter** - detects power meter for device in either w or kw.
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C1 smart power outlet with maxReportTime of 10 min.
|
||||
Check-in interval is double the value of maxReportTime for Zigbee device.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 2*10 = 20 min
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [SmartPower Outlet Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/201084854-SmartPower-Outlet)
|
||||
* [Samsung SmartThings Outlet Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205957620)
|
||||
@@ -101,12 +101,6 @@ def parse(String description) {
|
||||
else {
|
||||
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
||||
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 {
|
||||
@@ -126,15 +120,7 @@ def on() {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.onOffRefresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -142,8 +128,10 @@ def refresh() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
|
||||
zigbee.onOffConfig() + powerConfig() + refresh()
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
// 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)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,44 @@
|
||||
# Smartsense Moisture Sensor
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Samsung SmartThings Moisture Sensor](https://shop.smartthings.com/#!/products/samsung-smartthings-water-leak-sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Temperature Measurement** - defines device measures current temperature
|
||||
* **Water Sensor** - can detect presence of water (dry or wet)
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C2 moisture sensor with maxReportTime of 1 hr.
|
||||
Check-in interval is double the value of maxReportTime for Zigbee device.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 2*60 = 120 min
|
||||
|
||||
## Battery Specification
|
||||
|
||||
One CR2 3V battery required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the different sensors from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [SmartSense Moisture Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202847044-SmartSense-Moisture-Sensor)
|
||||
* [Samsung SmartThings Water Leak Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205957630)
|
||||
Other troubleshooting tips are listed as follows:
|
||||
* [Troubleshooting: Samsung SmartThings Water Leak Sensor won’t pair after removing pull-tab](https://support.smartthings.com/hc/en-us/articles/204966616-Troubleshooting-Samsung-SmartThings-device-won-t-pair-after-removing-pull-tab)
|
||||
@@ -101,13 +101,6 @@ def parse(String 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"
|
||||
def result = map ? createEvent(map) : null
|
||||
|
||||
@@ -233,6 +226,8 @@ private Map getBatteryResult(rawValue) {
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||
}
|
||||
@@ -259,7 +254,8 @@ private Map getTemperatureResult(value) {
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText,
|
||||
translatable: true
|
||||
translatable: true,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
@@ -282,15 +278,7 @@ private Map getMoistureResult(value) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -304,23 +292,19 @@ def refresh() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
|
||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||
def configCmds = [
|
||||
def enrollCmds = [
|
||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 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} ${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
|
||||
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
// 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() {
|
||||
|
||||
@@ -9,7 +9,8 @@ Works with:
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health]($health)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
|
||||
## Capabilities
|
||||
|
||||
@@ -21,10 +22,24 @@ Works with:
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C2 motion sensor that has 120min check-in interval
|
||||
|
||||
|
||||
A Category C2 motion sensor with maxReportTime of 1 hr.
|
||||
Check-in interval is double the value of maxReportTime for Zigbee device.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 2*60 = 120 min
|
||||
|
||||
## Battery Specification
|
||||
|
||||
One CR2477 (for Samsung SmartThings Motion Sensor) / CR123A (SmartSense Motion Sensor) 3V battery is required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [SmartSense Motion Sensor (original model) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200903280-SmartSense-Motion-Sensor-original-model-)
|
||||
* [SmartSense Motion Sensor (2014 model) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/203077520-SmartSense-Motion-Sensor-2014-model-)
|
||||
* [Samsung SmartThings Motion Sensor (2015 model) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205957580-Samsung-SmartThings-Motion-Sensor-2015-model-)
|
||||
Other troubleshooting tips are listed as follows:
|
||||
* [Troubleshooting: Samsung SmartThings Motion Sensor is stuck showing "Motion Detected" or "No Motion"](https://support.smartthings.com/hc/en-us/articles/200961130-Troubleshooting-Samsung-SmartThings-Motion-Sensor-is-stuck-showing-Motion-Detected-or-No-Motion-)
|
||||
* [Troubleshooting: Samsung SmartThings Motion Sensor won’t pair after removing pull-tab](https://support.smartthings.com/hc/en-us/articles/204966616-Troubleshooting-Samsung-SmartThings-device-won-t-pair-after-removing-pull-tab)
|
||||
|
||||
@@ -105,13 +105,6 @@ def parse(String 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"
|
||||
def result = map ? createEvent(map) : null
|
||||
|
||||
@@ -248,6 +241,8 @@ private Map getBatteryResult(rawValue) {
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||
}
|
||||
@@ -274,7 +269,8 @@ private Map getTemperatureResult(value) {
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText,
|
||||
translatable: true
|
||||
translatable: true,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
@@ -293,15 +289,7 @@ private Map getMotionResult(value) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -315,24 +303,19 @@ def refresh() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
|
||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||
|
||||
def configCmds = [
|
||||
def enrollCmds = [
|
||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||
"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"
|
||||
]
|
||||
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 enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
|
||||
}
|
||||
|
||||
def enrollResponse() {
|
||||
|
||||
@@ -170,7 +170,7 @@ private Map parseCustomMessage(String description) {
|
||||
|
||||
private Map parseIasMessage(String description) {
|
||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||
return zs.isAlarm1Set() ? getMotionResult('active') : getMotionResult('inactive')
|
||||
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive')
|
||||
}
|
||||
|
||||
def getTemperature(value) {
|
||||
@@ -206,6 +206,8 @@ private Map getBatteryResult(rawValue) {
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||
}
|
||||
@@ -226,7 +228,8 @@ private Map getTemperatureResult(value) {
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText
|
||||
descriptionText: descriptionText,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,45 @@
|
||||
# Smartsense Multi Sensor
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Samsung SmartThings Multi Sensor](https://shop.smartthings.com/#!/products/smartsense-multi)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Three Axis** - monitors the state of a single axis
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Sensor** - detects sensor events
|
||||
* **Contact Sensor** - can detect contact (possible values: open,closed)
|
||||
* **Acceleration Sensor** - allows for acceleration detection.
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Temperature Measurement** - defines device measures current temperature
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C2 multi sensor with maxReportTime of 1 hr.
|
||||
Check-in interval is double the value of maxReportTime for Zigbee device.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 2*60 = 120 min
|
||||
|
||||
## Battery Specification
|
||||
|
||||
One CR2450 (for Samsung SmartThings Multipurpose Sensor) battery / Two AAAA (for SmartSense Multi Sensor) batteries required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Other troubleshooting tips are listed as follows:
|
||||
* [Troubleshooting: Samsung SmartThings Multipurpose Sensor is stuck on "open" or "closed"](https://support.smartthings.com/hc/en-us/articles/200955940-Troubleshooting-Samsung-SmartThings-Multipurpose-Sensor-is-stuck-on-open-or-closed-)
|
||||
* [Troubleshooting: Temperature reading for the Samsung SmartThings Multipurpose Sensor is off](https://support.smartthings.com/hc/en-us/articles/200756845-Troubleshooting-Temperature-reading-for-the-Samsung-SmartThings-Multipurpose-Sensor-is-off)
|
||||
* [Troubleshooting: Samsung SmartThings Multipurpose Sensor won’t pair after removing pull-tab](https://support.smartthings.com/hc/en-us/articles/204966616-Troubleshooting-Samsung-SmartThings-device-won-t-pair-after-removing-pull-tab)
|
||||
@@ -127,13 +127,6 @@ def parse(String 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
|
||||
|
||||
if (description?.startsWith('enroll request')) {
|
||||
@@ -313,6 +306,8 @@ private Map getBatteryResult(rawValue) {
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||
}
|
||||
@@ -333,10 +328,11 @@ private Map getTemperatureResult(value) {
|
||||
'{{ device.displayName }} was {{ value }}°F'
|
||||
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText,
|
||||
translatable: true
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText,
|
||||
translatable: true,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
@@ -375,15 +371,7 @@ private getAccelerationResult(numValue) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -413,13 +401,16 @@ def refresh() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
|
||||
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() +
|
||||
zigbee.batteryConfig() +
|
||||
zigbee.temperatureConfig() +
|
||||
zigbee.temperatureConfig(30, 300) +
|
||||
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
||||
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||
|
||||
@@ -206,6 +206,8 @@ def getTemperature(value) {
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||
}
|
||||
@@ -223,9 +225,10 @@ def getTemperature(value) {
|
||||
}
|
||||
def descriptionText = "${linkText} was ${value}°${temperatureScale}"
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,41 @@
|
||||
# Smartsense Open/Closed Sensor
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Samsung SmartThings Open/Closed Sensor](https://shop.smartthings.com/#!/packs/smartsense-open-closed-sensor/)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Contact Sensor** - can detect contact (possible values: open,closed)
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Temperature Measurement** - defines device measures current temperature
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C2 open/closed sensor with maxReportTime of 1 hr.
|
||||
Check-in interval is double the value of maxReportTime for Zigbee device.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 2*60 = 120 min
|
||||
|
||||
## Battery Specification
|
||||
|
||||
One CR2 3V battery required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the sensor from SmartThings can be found in the following link:
|
||||
* [SmartSense Open/Closed Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202836844-SmartSense-Open-Closed-Sensor)
|
||||
@@ -92,13 +92,6 @@ def parse(String 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"
|
||||
def result = map ? createEvent(map) : null
|
||||
|
||||
@@ -207,6 +200,8 @@ private Map getBatteryResult(rawValue) {
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||
}
|
||||
@@ -226,7 +221,8 @@ private Map getTemperatureResult(value) {
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText
|
||||
descriptionText: descriptionText,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
@@ -245,15 +241,7 @@ private Map getContactResult(value) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -267,23 +255,19 @@ def refresh() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
|
||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||
def configCmds = [
|
||||
def enrollCmds = [
|
||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 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} ${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
|
||||
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
// 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() {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,41 @@
|
||||
# SmartSense Temp/Humidity Sensor
|
||||
|
||||
|
||||
|
||||
Works with:
|
||||
|
||||
* [Samsung SmartSense Temp/Humidity Sensor](https://shop.smartthings.com/#!/products/smartsense-temp-humidity-sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Relative Humidity Measurement** - defines device measures relative humidity
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Temperature Measurement** - defines device measures current temperature
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C2 SmartSense Temp/Humidity Sensor with maxReportTime of 1 hr.
|
||||
Check-in interval is double the value of maxReportTime for Zigbee device.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 2*60 = 120 min
|
||||
|
||||
## Battery Specification
|
||||
|
||||
One CR2 battery is required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the sensor from SmartThings can be found in the following link:
|
||||
* [Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/203040294)
|
||||
@@ -83,13 +83,6 @@ def parse(String 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"
|
||||
return map ? createEvent(map) : null
|
||||
}
|
||||
@@ -214,6 +207,8 @@ private Map getBatteryResult(rawValue) {
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||
}
|
||||
@@ -233,7 +228,8 @@ private Map getTemperatureResult(value) {
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText
|
||||
descriptionText: descriptionText,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
@@ -250,14 +246,7 @@ private Map getHumidityResult(value) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
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)
|
||||
}
|
||||
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||
}
|
||||
|
||||
def refresh()
|
||||
@@ -275,23 +264,19 @@ def refresh()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
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",
|
||||
|
||||
def humidityConfigCmds = [
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
|
||||
"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) {
|
||||
|
||||
@@ -213,7 +213,8 @@ private Map getTemperatureResult(value) {
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText
|
||||
descriptionText: descriptionText,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -54,12 +54,6 @@ def parse(String description) {
|
||||
|
||||
def event = zigbee.getEvent(description)
|
||||
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) {}
|
||||
else {
|
||||
sendEvent(event)
|
||||
@@ -86,15 +80,7 @@ def setLevel(value) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.onOffRefresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -103,7 +89,8 @@ def refresh() {
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
||||
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
@@ -85,12 +85,6 @@ def parse(String description) {
|
||||
def event = zigbee.getEvent(description)
|
||||
if (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) {}
|
||||
else {
|
||||
if (event.name=="colorTemperature") {
|
||||
@@ -105,12 +99,12 @@ def parse(String description) {
|
||||
|
||||
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
||||
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
|
||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
|
||||
}
|
||||
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "saturation", value: saturationValue, displayed:false)
|
||||
sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false)
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -130,15 +124,7 @@ def off() {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.onOffRefresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -147,9 +133,10 @@ def refresh() {
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
||||
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
||||
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)
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
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) {
|
||||
|
||||
@@ -36,6 +36,7 @@ metadata {
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Recessed Kit RT 5/6 Tunable White"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic B40 TW - LIGHTIFY", deviceJoinName: "OSRAM LIGHTIFY Classic B40 Tunable White"
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
@@ -73,12 +74,6 @@ def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
def event = zigbee.getEvent(description)
|
||||
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) {}
|
||||
else {
|
||||
if (event.name=="colorTemperature") {
|
||||
@@ -109,15 +104,7 @@ def setLevel(value) {
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
|
||||
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)
|
||||
}
|
||||
return zigbee.onOffRefresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
@@ -126,9 +113,10 @@ def refresh() {
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
||||
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
|
||||
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
def setColorTemperature(value) {
|
||||
|
||||
@@ -65,7 +65,16 @@ void updateSwitch() {
|
||||
private void updateAll(devices) {
|
||||
def command = request.JSON?.command
|
||||
if (command) {
|
||||
devices."$command"()
|
||||
switch(command) {
|
||||
case "on":
|
||||
devices.on()
|
||||
break
|
||||
case "off":
|
||||
devices.off()
|
||||
break
|
||||
default:
|
||||
httpError(403, "Access denied. This command is not supported by current capability.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +86,16 @@ private void update(devices) {
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
device."$command"()
|
||||
switch(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()) {
|
||||
|
||||
def redirectUrl = getBuildRedirectUrl()
|
||||
log.debug "Redirect url = ${redirectUrl}"
|
||||
// log.debug "Redirect url = ${redirectUrl}"
|
||||
|
||||
if (state.authToken) {
|
||||
description = "Tap 'Next' to proceed"
|
||||
@@ -113,13 +113,13 @@ def oauthInitUrl() {
|
||||
scope: "read_station"
|
||||
]
|
||||
|
||||
log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
|
||||
// log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
|
||||
|
||||
redirect (location: getVendorAuthPath() + toQueryString(oauthParams))
|
||||
}
|
||||
|
||||
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 oauthState = params.state
|
||||
@@ -135,7 +135,7 @@ def callback() {
|
||||
scope: "read_station"
|
||||
]
|
||||
|
||||
log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
|
||||
// log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
|
||||
|
||||
def tokenUrl = getVendorTokenPath()
|
||||
def params = [
|
||||
@@ -144,7 +144,7 @@ def callback() {
|
||||
body: tokenParams
|
||||
]
|
||||
|
||||
log.debug "PARAMS: ${params}"
|
||||
// log.debug "PARAMS: ${params}"
|
||||
|
||||
httpPost(params) { resp ->
|
||||
|
||||
@@ -156,7 +156,7 @@ def callback() {
|
||||
state.refreshToken = data.refresh_token
|
||||
state.authToken = data.access_token
|
||||
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 ->
|
||||
def data = slurper.parseText(key);
|
||||
log.debug "Data: $data"
|
||||
// log.debug "Data: $data"
|
||||
|
||||
state.refreshToken = data.refresh_token
|
||||
state.accessToken = data.access_token
|
||||
|
||||
@@ -26,9 +26,9 @@ preferences {
|
||||
}
|
||||
|
||||
section("Temperature monitor?") {
|
||||
input "temp", "capability.temperatureMeasurement", title: "Temp Sensor", required: false
|
||||
input "maxTemp", "number", title: "Max Temp?", required: false
|
||||
input "minTemp", "number", title: "Min Temp?", required: false
|
||||
input "temp", "capability.temperatureMeasurement", title: "Temperature Sensor", required: false
|
||||
input "maxTemp", "number", title: "Max Temperature (°${location.temperatureScale})", required: false
|
||||
input "minTemp", "number", title: "Min Temperature (°${location.temperatureScale})", required: false
|
||||
}
|
||||
|
||||
section("When which people are away?") {
|
||||
|
||||
@@ -62,7 +62,7 @@ def initialize() {
|
||||
}
|
||||
|
||||
def sendit(evt) {
|
||||
log.debug "$evt.value: $evt, $settings"
|
||||
log.debug "$evt.value: $evt"
|
||||
sendMessage()
|
||||
}
|
||||
|
||||
@@ -80,6 +80,6 @@ def sendMessage() {
|
||||
sendSms phone3, msg
|
||||
}
|
||||
if (!phone1 && !phone2 && !phone3) {
|
||||
sendPush msg
|
||||
sendPush msg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
definition(
|
||||
name: "Smart Home Ventilation",
|
||||
namespace: "MichaelStruck",
|
||||
@@ -164,7 +164,7 @@ def installed() {
|
||||
def updated() {
|
||||
unschedule()
|
||||
turnOffSwitch() //Turn off all switches if the schedules are changed while in mid-schedule
|
||||
unsubscribe
|
||||
unsubscribe()
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
init()
|
||||
}
|
||||
@@ -174,12 +174,12 @@ def init() {
|
||||
schedule (midnightTime, midNight)
|
||||
subscribe(location, "mode", locationHandler)
|
||||
startProcess()
|
||||
}
|
||||
}
|
||||
|
||||
// Common methods
|
||||
|
||||
def startProcess () {
|
||||
createDayArray()
|
||||
createDayArray()
|
||||
state.dayCount=state.data.size()
|
||||
if (state.dayCount){
|
||||
state.counter = 0
|
||||
@@ -190,7 +190,7 @@ def startProcess () {
|
||||
def startDay() {
|
||||
def start = convertEpoch(state.data[state.counter].start)
|
||||
def stop = convertEpoch(state.data[state.counter].stop)
|
||||
|
||||
|
||||
runOnce(start, turnOnSwitch, [overwrite: true])
|
||||
runOnce(stop, incDay, [overwrite: true])
|
||||
}
|
||||
@@ -218,7 +218,7 @@ def locationHandler(evt) {
|
||||
}
|
||||
if (!result) {
|
||||
startProcess()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def midNight(){
|
||||
@@ -238,7 +238,7 @@ def turnOffSwitch() {
|
||||
}
|
||||
log.debug "Home ventilation switches are off."
|
||||
}
|
||||
|
||||
|
||||
def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
||||
def title = ""
|
||||
def dayListClean = "On "
|
||||
@@ -252,7 +252,7 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
||||
dayListClean = "${dayListClean}, "
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dayListClean = "Every day"
|
||||
}
|
||||
@@ -272,7 +272,7 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
||||
modeListClean = "${modeListClean} ${modePrefix}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
modeListClean = "${modeListClean}all modes"
|
||||
}
|
||||
@@ -283,16 +283,16 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
||||
title += "\nSchedule 2: ${humanReadableTime(on2)} to ${humanReadableTime(off2)}"
|
||||
}
|
||||
if (on3 && off3) {
|
||||
title += "\nSchedule 3: ${humanReadableTime(on3)} to ${humanReadableTime(off3)}"
|
||||
title += "\nSchedule 3: ${humanReadableTime(on3)} to ${humanReadableTime(off3)}"
|
||||
}
|
||||
if (on4 && off4) {
|
||||
title += "\nSchedule 4: ${humanReadableTime(on4)} to ${humanReadableTime(off4)}"
|
||||
title += "\nSchedule 4: ${humanReadableTime(on4)} to ${humanReadableTime(off4)}"
|
||||
}
|
||||
if (on1 || on2 || on3 || on4) {
|
||||
title += "\n$modeListClean"
|
||||
title += "\n$dayListClean"
|
||||
title += "\n$dayListClean"
|
||||
}
|
||||
|
||||
|
||||
if (!on1 && !on2 && !on3 && !on4) {
|
||||
title="Click to configure scenario"
|
||||
}
|
||||
@@ -374,7 +374,7 @@ def createDayArray() {
|
||||
timeOk(timeOnD1, timeOffD1)
|
||||
timeOk(timeOnD2, timeOffD2)
|
||||
timeOk(timeOnD3, timeOffD3)
|
||||
timeOk(timeOnD4, timeOffD4)
|
||||
timeOk(timeOnD4, timeOffD4)
|
||||
}
|
||||
}
|
||||
state.data.sort{it.start}
|
||||
@@ -384,7 +384,7 @@ def createDayArray() {
|
||||
|
||||
private def textAppName() {
|
||||
def text = "Smart Home Ventilation"
|
||||
}
|
||||
}
|
||||
|
||||
private def textVersion() {
|
||||
def text = "Version 2.1.2 (05/31/2015)"
|
||||
@@ -416,4 +416,4 @@ private def textHelp() {
|
||||
"that each scenario does not overlap and run in separate modes (i.e. Home, Out of town, etc). Also note that you should " +
|
||||
"avoid scheduling the ventilation fan at exactly midnight; the app resets itself at that time. It is suggested to start any new schedule " +
|
||||
"at 12:15 am or later."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ definition(
|
||||
name: "Monitor on Sense",
|
||||
namespace: "resteele",
|
||||
author: "Rachel Steele",
|
||||
description: "Turn on Monitor when vibration is sensed",
|
||||
description: "Turn on switch when vibration is sensed",
|
||||
category: "My Apps",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
@@ -25,10 +25,10 @@ definition(
|
||||
|
||||
|
||||
preferences {
|
||||
section("When the keyboard is used...") {
|
||||
section("When vibration is sensed...") {
|
||||
input "accelerationSensor", "capability.accelerationSensor", title: "Which Sensor?"
|
||||
}
|
||||
section("Turn on/off a light...") {
|
||||
section("Turn on switch...") {
|
||||
input "switch1", "capability.switch"
|
||||
}
|
||||
}
|
||||
@@ -47,5 +47,3 @@ def updated() {
|
||||
def accelerationActiveHandler(evt) {
|
||||
switch1.on()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -114,13 +114,16 @@ def beaconHandler(evt) {
|
||||
|
||||
if (allOk) {
|
||||
def data = new groovy.json.JsonSlurper().parseText(evt.data)
|
||||
log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
|
||||
// removed logging of device names. can be added back for debugging
|
||||
//log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
|
||||
|
||||
def beaconName = getBeaconName(evt)
|
||||
log.debug "<beacon-control> beaconName: $beaconName"
|
||||
// removed logging of device names. can be added back for debugging
|
||||
//log.debug "<beacon-control> beaconName: $beaconName"
|
||||
|
||||
def phoneName = getPhoneName(data)
|
||||
log.debug "<beacon-control> phoneName: $phoneName"
|
||||
// removed logging of device names. can be added back for debugging
|
||||
//log.debug "<beacon-control> phoneName: $phoneName"
|
||||
if (phoneName != null) {
|
||||
def action = data.presence == "1" ? "arrived" : "left"
|
||||
def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"
|
||||
|
||||
@@ -49,13 +49,15 @@ preferences {
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
||||
// 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}}"
|
||||
subscribe(people, "presence", presence)
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
|
||||
// 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}}"
|
||||
unsubscribe()
|
||||
subscribe(people, "presence", presence)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,10 +64,12 @@ def meterHandler(evt) {
|
||||
def lastValue = atomicState.lastValue as double
|
||||
atomicState.lastValue = meterValue
|
||||
|
||||
def dUnit = evt.unit ?: "Watts"
|
||||
|
||||
def aboveThresholdValue = aboveThreshold as int
|
||||
if (meterValue > aboveThresholdValue) {
|
||||
if (lastValue < aboveThresholdValue) { // only send notifications when crossing the threshold
|
||||
def msg = "${meter} reported ${evt.value} ${evt.unit} which is above your threshold of ${aboveThreshold}."
|
||||
def msg = "${meter} reported ${evt.value} ${dUnit} which is above your threshold of ${aboveThreshold}."
|
||||
sendMessage(msg)
|
||||
} else {
|
||||
// log.debug "not sending notification for ${evt.description} because the threshold (${aboveThreshold}) has already been crossed"
|
||||
@@ -78,7 +80,7 @@ def meterHandler(evt) {
|
||||
def belowThresholdValue = belowThreshold as int
|
||||
if (meterValue < belowThresholdValue) {
|
||||
if (lastValue > belowThresholdValue) { // only send notifications when crossing the threshold
|
||||
def msg = "${meter} reported ${evt.value} ${evt.unit} which is below your threshold of ${belowThreshold}."
|
||||
def msg = "${meter} reported ${evt.value} ${dUnit} which is below your threshold of ${belowThreshold}."
|
||||
sendMessage(msg)
|
||||
} else {
|
||||
// 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
|
||||
|
||||
if (alreadySentSms) {
|
||||
log.debug "SMS already sent to $phone within the last $deltaSeconds seconds"
|
||||
log.debug "SMS already sent within the last $deltaSeconds seconds"
|
||||
} else {
|
||||
def msg = "${alarm.displayName} is wet!"
|
||||
log.debug "$alarm is wet, texting $phone"
|
||||
log.debug "$alarm is wet, texting phone number"
|
||||
|
||||
if (location.contactBookEnabled) {
|
||||
sendNotificationToContacts(msg, recipients)
|
||||
|
||||
@@ -90,7 +90,7 @@ def takeAction(){
|
||||
}
|
||||
|
||||
def sendTextMessage() {
|
||||
log.debug "$multisensor was open too long, texting $phone"
|
||||
log.debug "$multisensor was open too long, texting phone"
|
||||
|
||||
updateSmsHistory()
|
||||
def openMinutes = maxOpenTime * (state.smsHistory?.size() ?: 1)
|
||||
|
||||
@@ -761,7 +761,7 @@ String displayableTime(timeRemaining) {
|
||||
return "${minutes}:00"
|
||||
}
|
||||
def fraction = "0.${parts[1]}" as double
|
||||
def seconds = "${60 * fraction as int}".padRight(2, "0")
|
||||
def seconds = "${60 * fraction as int}".padLeft(2, "0")
|
||||
return "${minutes}:${seconds}"
|
||||
}
|
||||
|
||||
@@ -1101,4 +1101,4 @@ def hasStartLevel() {
|
||||
|
||||
def hasEndLevel() {
|
||||
return (endLevel != null && endLevel != "")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ preferences {
|
||||
|
||||
def installed() {
|
||||
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)
|
||||
}
|
||||
|
||||
def updated() {
|
||||
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()
|
||||
subscribe(people, "presence", presence)
|
||||
}
|
||||
@@ -71,11 +71,10 @@ def presence(evt)
|
||||
def person = getPerson(evt)
|
||||
def recentNotPresent = person.statesSince("presence", t0).find{it.value == "not present"}
|
||||
if (recentNotPresent) {
|
||||
log.debug "skipping notification of arrival of ${person.displayName} because last departure was only ${now() - recentNotPresent.date.time} msec ago"
|
||||
log.debug "skipping notification of arrival of Person because last departure was only ${now() - recentNotPresent.date.time} msec ago"
|
||||
}
|
||||
else {
|
||||
def message = "${person.displayName} arrived at home, changing mode to '${newMode}'"
|
||||
log.info message
|
||||
send(message)
|
||||
setLocationMode(newMode)
|
||||
}
|
||||
@@ -106,6 +105,4 @@ private send(msg) {
|
||||
sendSms(phone, msg)
|
||||
}
|
||||
}
|
||||
|
||||
log.debug msg
|
||||
}
|
||||
|
||||
@@ -57,12 +57,11 @@ def scheduleCheck()
|
||||
def message = message1 ?: "SmartThings - Habit Helper Reminder!"
|
||||
|
||||
if (location.contactBookEnabled) {
|
||||
log.debug "Texting reminder: ($message) to contacts:${recipients?.size()}"
|
||||
log.debug "Texting reminder to contacts:${recipients?.size()}"
|
||||
sendNotificationToContacts(message, recipients)
|
||||
}
|
||||
else {
|
||||
|
||||
log.debug "Texting reminder: ($message) to $phone1"
|
||||
log.debug "Texting reminder"
|
||||
sendSms(phone1, message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,7 @@ def bridgeDiscoveryFailed() {
|
||||
}
|
||||
}
|
||||
|
||||
def bridgeLinking()
|
||||
{
|
||||
def bridgeLinking() {
|
||||
int linkRefreshcount = !state.linkRefreshcount ? 0 : state.linkRefreshcount as int
|
||||
state.linkRefreshcount = linkRefreshcount + 1
|
||||
def refreshInterval = 3
|
||||
@@ -171,7 +170,7 @@ def bulbDiscovery() {
|
||||
|
||||
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.") {
|
||||
input "selectedBulbs", "enum", required:false, title:"Select Hue Lights to add (${numFound} found)", multiple:true, options:newLights
|
||||
input "selectedBulbs", "enum", required:false, title:"Select Hue Lights to add (${numFound} found)", multiple:true, submitOnChange: true, options:newLights
|
||||
paragraph title: "Previously added Hue Lights (${existingLights.size()} added)", existingLightsDescription
|
||||
}
|
||||
section {
|
||||
@@ -328,7 +327,7 @@ def bulbListHandler(hub, data = "") {
|
||||
def object = new groovy.json.JsonSlurper().parseText(data)
|
||||
object.each { k,v ->
|
||||
if (v instanceof Map)
|
||||
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub]
|
||||
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub, online: v.state?.reachable]
|
||||
}
|
||||
}
|
||||
def bridge = null
|
||||
@@ -448,7 +447,6 @@ def addBridge() {
|
||||
updateBridgeStatus(childDevice)
|
||||
childDevice.sendEvent(name: "idNumber", value: idNumber)
|
||||
|
||||
|
||||
if (vbridge.value.ip && vbridge.value.port) {
|
||||
if (vbridge.value.ip.contains(".")) {
|
||||
childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port)
|
||||
@@ -649,8 +647,7 @@ def locationHandler(evt) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parsedEvent.headers && parsedEvent.body) {
|
||||
} else if (parsedEvent.headers && parsedEvent.body) {
|
||||
log.trace "HUE BRIDGE RESPONSES"
|
||||
def headerString = parsedEvent.headers.toString()
|
||||
if (headerString?.contains("xml")) {
|
||||
@@ -727,13 +724,13 @@ private void updateBridgeStatus(childDevice) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. Also, set ID number on bridge if not done previously.
|
||||
* Check if all Hue bridges have been heard from in the last 11 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.
|
||||
*/
|
||||
private void checkBridgeStatus() {
|
||||
def bridges = getHueBridges()
|
||||
// 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 * 16)
|
||||
// Check if each bridge has been heard from within the last 11 minutes (2 poll intervals times 5 minutes plus buffer)
|
||||
def time = now() - (1000 * 60 * 11)
|
||||
bridges.each {
|
||||
def d = getChildDevice(it.value.mac)
|
||||
if(d) {
|
||||
@@ -743,14 +740,21 @@ private void checkBridgeStatus() {
|
||||
d.sendEvent(name: "idNumber", value: it.value.idNumber)
|
||||
}
|
||||
|
||||
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
|
||||
log.warn "Bridge $it.key is Offline"
|
||||
d.sendEvent(name: "status", value: "Offline")
|
||||
} else {
|
||||
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
|
||||
log.warn "Bridge $it.key is Offline"
|
||||
d.sendEvent(name: "status", value: "Offline")
|
||||
|
||||
state.bulbs?.each {
|
||||
it.value.online = false
|
||||
}
|
||||
getChildDevices().each {
|
||||
it.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", isStateChange: true, displayed: false)
|
||||
}
|
||||
} else {
|
||||
d.sendEvent(name: "status", value: "Online")//setOnline(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def isValidSource(macAddress) {
|
||||
@@ -758,6 +762,10 @@ def isValidSource(macAddress) {
|
||||
return (vbridges?.find {"${it.value.mac}" == macAddress}) != null
|
||||
}
|
||||
|
||||
def isInBulbDiscovery() {
|
||||
return state.inBulbDiscovery
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
//CHILD DEVICE METHODS
|
||||
/////////////////////////////////////
|
||||
@@ -781,8 +789,7 @@ def parse(childDevice, description) {
|
||||
if (body instanceof java.util.Map) {
|
||||
// get (poll) reponse
|
||||
return handlePoll(body)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
//put response
|
||||
return handleCommandResponse(body)
|
||||
}
|
||||
@@ -879,36 +886,40 @@ private handleCommandResponse(body) {
|
||||
// scan entire response before sending events to make sure they are always in the same order
|
||||
def updates = [:]
|
||||
|
||||
body.each { payload ->
|
||||
log.debug $payload
|
||||
body.each { payload ->
|
||||
log.debug $payload
|
||||
|
||||
if (payload?.success) {
|
||||
def childDeviceNetworkId = app.id + "/"
|
||||
def eventType
|
||||
def childDeviceNetworkId = app.id + "/"
|
||||
def eventType
|
||||
payload.success.each { k, v ->
|
||||
def data = k.split("/")
|
||||
if (data.length == 5) {
|
||||
childDeviceNetworkId = app.id + "/" + k.split("/")[2]
|
||||
if (!updates[childDeviceNetworkId])
|
||||
updates[childDeviceNetworkId] = [:]
|
||||
eventType = k.split("/")[4]
|
||||
eventType = k.split("/")[4]
|
||||
updates[childDeviceNetworkId]."$eventType" = v
|
||||
}
|
||||
}
|
||||
} else if (payload.error) {
|
||||
log.warn "Error returned from Hue bridge error = ${body?.error}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send events for each update found above (order of events should be same as handlePoll())
|
||||
updates.each { childDeviceNetworkId, params ->
|
||||
def device = getChildDevice(childDeviceNetworkId)
|
||||
sendBasicEvents(device, "on", params.on)
|
||||
sendBasicEvents(device, "bri", params.bri)
|
||||
sendColorEvents(device, params.xy, params.hue, params.sat, params.ct)
|
||||
}
|
||||
def id = getId(device)
|
||||
// If device is offline, then don't send events which will update device watch
|
||||
if (isOnline(id)) {
|
||||
sendBasicEvents(device, "on", params.on)
|
||||
sendBasicEvents(device, "bri", params.bri)
|
||||
sendColorEvents(device, params.xy, params.hue, params.sat, params.ct)
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a response to a poll (GET) sent to the Hue Bridge.
|
||||
@@ -928,26 +939,33 @@ private handleCommandResponse(body) {
|
||||
* @return empty array
|
||||
*/
|
||||
private handlePoll(body) {
|
||||
if (state.updating) {
|
||||
// If user just executed commands, then ignore poll to not confuse the turning on/off state
|
||||
return []
|
||||
}
|
||||
|
||||
def bulbs = getChildDevices()
|
||||
for (bulb in body) {
|
||||
def device = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"}
|
||||
if (device) {
|
||||
if (bulb.value.state?.reachable) {
|
||||
sendBasicEvents(device, "on", bulb.value?.state?.on)
|
||||
sendBasicEvents(device, "bri", bulb.value?.state?.bri)
|
||||
sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode)
|
||||
if (state.bulbs[bulb.key]?.online == false) {
|
||||
// light just came back online, notify device watch
|
||||
def lastActivity = now()
|
||||
device.sendEvent(name: "deviceWatch-status", value: "ONLINE", description: "Last Activity is on ${new Date((long) lastActivity)}", displayed: false, isStateChange: true)
|
||||
}
|
||||
state.bulbs[bulb.key]?.online = true
|
||||
|
||||
// If user just executed commands, then do not send events to avoid confusing the turning on/off state
|
||||
if (!state.updating) {
|
||||
sendBasicEvents(device, "on", bulb.value?.state?.on)
|
||||
sendBasicEvents(device, "bri", bulb.value?.state?.bri)
|
||||
sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode)
|
||||
}
|
||||
} else {
|
||||
state.bulbs[bulb.key]?.online = false
|
||||
log.warn "$device is not reachable by Hue bridge"
|
||||
}
|
||||
}
|
||||
}
|
||||
return []
|
||||
device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
private updateInProgress() {
|
||||
state.updating = true
|
||||
@@ -976,22 +994,34 @@ def hubVerification(bodytext) {
|
||||
|
||||
def on(childDevice) {
|
||||
log.debug "Executing 'on'"
|
||||
def id = getId(childDevice)
|
||||
if (!isOnline(id)) {
|
||||
return "Bulb is unreachable"
|
||||
}
|
||||
updateInProgress()
|
||||
createSwitchEvent(childDevice, "on")
|
||||
put("lights/${getId(childDevice)}/state", [on: true])
|
||||
put("lights/$id/state", [on: true])
|
||||
return "Bulb is turning On"
|
||||
}
|
||||
|
||||
def off(childDevice) {
|
||||
log.debug "Executing 'off'"
|
||||
def id = getId(childDevice)
|
||||
if (!isOnline(id)) {
|
||||
return "Bulb is unreachable"
|
||||
}
|
||||
updateInProgress()
|
||||
createSwitchEvent(childDevice, "off")
|
||||
put("lights/${getId(childDevice)}/state", [on: false])
|
||||
put("lights/$id/state", [on: false])
|
||||
return "Bulb is turning Off"
|
||||
}
|
||||
|
||||
def setLevel(childDevice, percent) {
|
||||
log.debug "Executing 'setLevel'"
|
||||
def id = getId(childDevice)
|
||||
if (!isOnline(id)) {
|
||||
return "Bulb is unreachable"
|
||||
}
|
||||
updateInProgress()
|
||||
// 1 - 254
|
||||
def level
|
||||
@@ -1006,48 +1036,64 @@ def setLevel(childDevice, percent) {
|
||||
// that means that the light will still be on when on is called next time
|
||||
// Lets emulate that here
|
||||
if (percent > 0) {
|
||||
put("lights/${getId(childDevice)}/state", [bri: level, on: true])
|
||||
put("lights/$id/state", [bri: level, on: true])
|
||||
} else {
|
||||
put("lights/${getId(childDevice)}/state", [on: false])
|
||||
put("lights/$id/state", [on: false])
|
||||
}
|
||||
return "Setting level to $percent"
|
||||
}
|
||||
|
||||
def setSaturation(childDevice, percent) {
|
||||
log.debug "Executing 'setSaturation($percent)'"
|
||||
updateInProgress()
|
||||
def id = getId(childDevice)
|
||||
if (!isOnline(id)) {
|
||||
return "Bulb is unreachable"
|
||||
}
|
||||
|
||||
updateInProgress()
|
||||
// 0 - 254
|
||||
def level = Math.min(Math.round(percent * 254 / 100), 254)
|
||||
// TODO should this be done by app only or should we default to on?
|
||||
createSwitchEvent(childDevice, "on")
|
||||
put("lights/${getId(childDevice)}/state", [sat: level, on: true])
|
||||
put("lights/$id/state", [sat: level, on: true])
|
||||
return "Setting saturation to $percent"
|
||||
}
|
||||
|
||||
def setHue(childDevice, percent) {
|
||||
log.debug "Executing 'setHue($percent)'"
|
||||
def id = getId(childDevice)
|
||||
if (!isOnline(id)) {
|
||||
return "Bulb is unreachable"
|
||||
}
|
||||
updateInProgress()
|
||||
// 0 - 65535
|
||||
def level = Math.min(Math.round(percent * 65535 / 100), 65535)
|
||||
// TODO should this be done by app only or should we default to on?
|
||||
createSwitchEvent(childDevice, "on")
|
||||
put("lights/${getId(childDevice)}/state", [hue: level, on: true])
|
||||
put("lights/$id/state", [hue: level, on: true])
|
||||
return "Setting hue to $percent"
|
||||
}
|
||||
|
||||
def setColorTemperature(childDevice, huesettings) {
|
||||
log.debug "Executing 'setColorTemperature($huesettings)'"
|
||||
def id = getId(childDevice)
|
||||
if (!isOnline(id)) {
|
||||
return "Bulb is unreachable"
|
||||
}
|
||||
updateInProgress()
|
||||
// 153 (6500K) to 500 (2000K)
|
||||
def ct = hueSettings == 6500 ? 153 : Math.round(1000000/huesettings)
|
||||
createSwitchEvent(childDevice, "on")
|
||||
put("lights/${getId(childDevice)}/state", [ct: ct, on: true])
|
||||
put("lights/$id/state", [ct: ct, on: true])
|
||||
return "Setting color temperature to $percent"
|
||||
}
|
||||
|
||||
def setColor(childDevice, huesettings) {
|
||||
log.debug "Executing 'setColor($huesettings)'"
|
||||
|
||||
def id = getId(childDevice)
|
||||
if (!isOnline(id)) {
|
||||
return "Bulb is unreachable"
|
||||
}
|
||||
updateInProgress()
|
||||
|
||||
def value = [:]
|
||||
@@ -1104,15 +1150,23 @@ def setColor(childDevice, huesettings) {
|
||||
value.on = false
|
||||
|
||||
createSwitchEvent(childDevice, value.on ? "on" : "off")
|
||||
put("lights/${getId(childDevice)}/state", value)
|
||||
put("lights/$id/state", value)
|
||||
return "Setting color to $value"
|
||||
}
|
||||
|
||||
def ping(childDevice) {
|
||||
if (isOnline(getId(childDevice))) {
|
||||
childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true)
|
||||
return "Device is Online"
|
||||
} else {
|
||||
return "Device is Offline"
|
||||
}
|
||||
}
|
||||
|
||||
private getId(childDevice) {
|
||||
if (childDevice.device?.deviceNetworkId?.startsWith("HUE")) {
|
||||
return childDevice.device?.deviceNetworkId[3..-1]
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return childDevice.device?.deviceNetworkId.split("/")[-1]
|
||||
}
|
||||
}
|
||||
@@ -1123,10 +1177,13 @@ private poll() {
|
||||
log.debug "GET: $host$uri"
|
||||
sendHubCommand(new physicalgraph.device.HubAction("""GET ${uri} HTTP/1.1
|
||||
HOST: ${host}
|
||||
|
||||
""", physicalgraph.device.Protocol.LAN, selectedHue))
|
||||
}
|
||||
|
||||
private isOnline(id) {
|
||||
return (state.bulbs[id].online != null && state.bulbs[id].online) || state.bulbs[id].online == null
|
||||
}
|
||||
|
||||
private put(path, body) {
|
||||
def host = getBridgeIP()
|
||||
def uri = "/api/${state.username}/$path"
|
||||
@@ -1194,7 +1251,7 @@ def convertBulbListToMap() {
|
||||
if (state.bulbs instanceof java.util.List) {
|
||||
def map = [:]
|
||||
state.bulbs.unique {it.id}.each { bulb ->
|
||||
map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub":bulb.hub]]
|
||||
map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub":bulb.hub, "online": bulb.online]]
|
||||
}
|
||||
state.bulbs = map
|
||||
}
|
||||
|
||||
@@ -53,14 +53,14 @@ def accelerationActiveHandler(evt) {
|
||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||
|
||||
if (alreadySentSms) {
|
||||
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
|
||||
log.debug "SMS already sent within the last $deltaSeconds seconds"
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
else {
|
||||
log.debug "$accelerationSensor has moved, texting $phone1"
|
||||
log.debug "accelerationSensor has moved, sending text message"
|
||||
sendSms(phone1, "${accelerationSensor.label ?: accelerationSensor.name} moved")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,10 +69,10 @@ def temperatureHandler(evt) {
|
||||
def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1
|
||||
|
||||
if (alreadySentSms) {
|
||||
log.debug "SMS already sent to $phone1 within the last $deltaMinutes minutes"
|
||||
log.debug "SMS already sent within the last $deltaMinutes minutes"
|
||||
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
||||
} else {
|
||||
log.debug "Temperature rose above $tooHot: sending SMS to $phone1 and activating $mySwitch"
|
||||
log.debug "Temperature rose above $tooHot: sending SMS and activating $mySwitch"
|
||||
def tempScale = location.temperatureScale ?: "F"
|
||||
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
|
||||
switch1?.on()
|
||||
|
||||
@@ -74,8 +74,6 @@ def authPage()
|
||||
|
||||
def redirectUrl = oauthInitUrl()
|
||||
|
||||
log.debug "RedirectURL = ${redirectUrl}"
|
||||
|
||||
return dynamicPage(name: "Credentials", title: "Life360", nextPage:"listCirclesPage", uninstall: uninstallOption, install:false) {
|
||||
section {
|
||||
href url:redirectUrl, style:"embedded", required:false, title:"Life360", description:description
|
||||
@@ -257,8 +255,6 @@ def initializeLife360Connection() {
|
||||
def oauthClientId = appSettings.clientId
|
||||
def oauthClientSecret = appSettings.clientSecret
|
||||
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
|
||||
initialize()
|
||||
|
||||
def username = settings.username
|
||||
@@ -269,8 +265,6 @@ def initializeLife360Connection() {
|
||||
def basicCredentials = "${oauthClientId}:${oauthClientSecret}"
|
||||
def encodedCredentials = basicCredentials.encodeAsBase64().toString()
|
||||
|
||||
log.debug "Encoded Creds: ${encodedCredentials}"
|
||||
|
||||
|
||||
// call life360, get OAUTH token using password flow, save
|
||||
// curl -X POST -H "Authorization: Basic cFJFcXVnYWJSZXRyZTRFc3RldGhlcnVmcmVQdW1hbUV4dWNyRUh1YzptM2ZydXBSZXRSZXN3ZXJFQ2hBUHJFOTZxYWtFZHI0Vg=="
|
||||
@@ -284,8 +278,6 @@ def initializeLife360Connection() {
|
||||
"username=${username}&"+
|
||||
"password=${password}"
|
||||
|
||||
log.debug "Post Body: ${postBody}"
|
||||
|
||||
def result = null
|
||||
|
||||
try {
|
||||
@@ -295,7 +287,6 @@ def initializeLife360Connection() {
|
||||
}
|
||||
if (result.data.access_token) {
|
||||
state.life360AccessToken = result.data.access_token
|
||||
log.debug "Access Token = ${state.life360AccessToken}"
|
||||
return true;
|
||||
}
|
||||
log.debug "Response=${result.data}"
|
||||
@@ -533,8 +524,6 @@ def createCircleSubscription() {
|
||||
|
||||
def postBody = "url=${hookUrl}"
|
||||
|
||||
log.debug "Post Body: ${postBody}"
|
||||
|
||||
def result = null
|
||||
|
||||
try {
|
||||
@@ -586,8 +575,6 @@ def updated() {
|
||||
|
||||
// log.debug "After Find Attempt."
|
||||
|
||||
log.debug "Member Id = ${member.id}, Name = ${member.firstName} ${member.lastName}, Email Address = ${member.loginEmail}"
|
||||
|
||||
// log.debug "External Id=${app.id}:${member.id}"
|
||||
|
||||
// create the device
|
||||
|
||||
@@ -50,9 +50,9 @@ def authPage() {
|
||||
}
|
||||
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 = "${apiServerUrl}"
|
||||
log.debug "app id: ${app.id}"
|
||||
log.debug "redirect url: ${redirectUrl}"
|
||||
// def redirectUrl = "${apiServerUrl}"
|
||||
// log.debug "app id: ${app.id}"
|
||||
// log.debug "redirect url: ${redirectUrl}"s
|
||||
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:true) {
|
||||
section {
|
||||
href(url:redirectUrl, required:true, title:"Connect to LIFX", description:"Tap here to connect your LIFX account")
|
||||
@@ -372,7 +372,7 @@ def updateDevices() {
|
||||
def childDevice = getChildDevice(device.id)
|
||||
selectors.add("${device.id}")
|
||||
if (!childDevice) {
|
||||
log.info("Adding device ${device.id}: ${device.product}")
|
||||
// log.info("Adding device ${device.id}: ${device.product}")
|
||||
def data = [
|
||||
label: device.label,
|
||||
level: Math.round((device.brightness ?: 1) * 100),
|
||||
|
||||
@@ -48,9 +48,9 @@ preferences {
|
||||
}
|
||||
section("Via a push notification and/or an SMS message"){
|
||||
input("recipients", "contact", title: "Send notifications to") {
|
||||
input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false
|
||||
input "phone", "phone", title: "Enter a phone number to get SMS", required: false
|
||||
paragraph "If outside the US please make sure to enter the proper country code"
|
||||
input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"]
|
||||
input "pushAndPhone", "enum", title: "Notify me via Push Notification", required: false, options: ["Yes", "No"]
|
||||
}
|
||||
}
|
||||
section("Minimum time between messages (optional, defaults to every message)") {
|
||||
@@ -111,19 +111,24 @@ private sendMessage(evt) {
|
||||
if (location.contactBookEnabled) {
|
||||
sendNotificationToContacts(msg, recipients, options)
|
||||
} else {
|
||||
if (!phone || pushAndPhone != 'No') {
|
||||
log.debug 'sending push'
|
||||
options.method = 'push'
|
||||
//sendPush(msg)
|
||||
}
|
||||
if (phone) {
|
||||
options.phone = phone
|
||||
log.debug 'sending SMS'
|
||||
//sendSms(phone, msg)
|
||||
if (pushAndPhone != 'No') {
|
||||
log.debug 'Sending push and SMS'
|
||||
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) {
|
||||
state[evt.deviceId] = now()
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ def updated() {
|
||||
|
||||
def presenceHandler(evt) {
|
||||
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}")
|
||||
} 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}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ def updated() {
|
||||
|
||||
def presenceHandler(evt) {
|
||||
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) {
|
||||
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}")
|
||||
}
|
||||
} 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) {
|
||||
sendNotificationToContacts("${presence.label ?: presence.name} has left the ${location}", recipients)
|
||||
|
||||
@@ -67,7 +67,7 @@ def updated() {
|
||||
}
|
||||
|
||||
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(cars, "presence", carPresence)
|
||||
|
||||
@@ -71,7 +71,7 @@ def updated() {
|
||||
private subscribeToEvents()
|
||||
{
|
||||
subscribe intrusionMotions, "motion", intruderMotion
|
||||
subscribe residentMotions, "motion", residentMotion
|
||||
// subscribe residentMotions, "motion", residentMotion
|
||||
subscribe intrusionContacts, "contact", contact
|
||||
subscribe alarms, "alarm", alarm
|
||||
subscribe(app, appTouch)
|
||||
@@ -156,6 +156,7 @@ def residentMotion(evt)
|
||||
// startReArmSequence()
|
||||
// }
|
||||
//}
|
||||
unsubscribe(residentMotions)
|
||||
}
|
||||
|
||||
def contact(evt)
|
||||
@@ -214,7 +215,7 @@ def checkForReArm()
|
||||
}
|
||||
else {
|
||||
log.warn "checkForReArm: lastIntruderMotion was null, unable to check for re-arming intrusion detection"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private startAlarmSequence()
|
||||
|
||||
@@ -48,7 +48,7 @@ def updated()
|
||||
|
||||
def contactOpenHandler(evt) {
|
||||
log.trace "$evt.value: $evt, $settings"
|
||||
log.debug "$contact1 was opened, texting $phone1"
|
||||
log.debug "$contact1 was opened, sending text"
|
||||
if (location.contactBookEnabled) {
|
||||
sendNotificationToContacts("Your ${contact1.label ?: contact1.name} was opened", recipients)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ def updated() {
|
||||
|
||||
def motionActiveHandler(evt) {
|
||||
log.trace "$evt.value: $evt, $settings"
|
||||
|
||||
|
||||
if (presence1.latestValue("presence") == "not present") {
|
||||
// Don't send a continuous stream of text messages
|
||||
def deltaSeconds = 10
|
||||
@@ -60,14 +60,14 @@ def motionActiveHandler(evt) {
|
||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||
|
||||
if (alreadySentSms) {
|
||||
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
|
||||
log.debug "SMS already sent within the last $deltaSeconds seconds"
|
||||
} else {
|
||||
if (location.contactBookEnabled) {
|
||||
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)
|
||||
}
|
||||
else {
|
||||
log.debug "$motion1 has moved while you were out, texting $phone1"
|
||||
log.debug "$motion1 has moved while you were out, sending text"
|
||||
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
|
||||
|
||||
if (alreadySentSms) {
|
||||
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
|
||||
log.debug "SMS already sent to phone within the last $deltaSeconds seconds"
|
||||
} else {
|
||||
if (location.contactBookEnabled) {
|
||||
sendNotificationToContacts("Gun case has moved!", recipients)
|
||||
}
|
||||
else {
|
||||
log.debug "$accelerationSensor has moved, texting $phone1"
|
||||
log.debug "$accelerationSensor has moved, texting phone"
|
||||
sendSms(phone1, "Gun case has moved!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ def authPage() {
|
||||
|
||||
def oauthInitUrl() {
|
||||
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
|
||||
state.oauth_request_token = token.oauth_token
|
||||
@@ -76,7 +76,7 @@ def getToken() {
|
||||
]
|
||||
def requestTokenBaseUrl = "https://oauth.withings.com/account/request_token"
|
||||
def url = buildSignedUrl(requestTokenBaseUrl, params)
|
||||
log.debug "getToken - url: $url"
|
||||
//log.debug "getToken - url: $url"
|
||||
|
||||
return getJsonFromUrl(url)
|
||||
}
|
||||
@@ -182,7 +182,7 @@ def exchangeToken() {
|
||||
|
||||
def requestTokenBaseUrl = "https://oauth.withings.com/account/access_token"
|
||||
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)
|
||||
|
||||
@@ -198,8 +198,8 @@ def exchangeToken() {
|
||||
|
||||
def load() {
|
||||
def json = get(getMeasurement(new Date() - 30))
|
||||
|
||||
log.debug "swapped, then received: $json"
|
||||
// removed logging of actual json payload. Can be put back for debugging
|
||||
log.debug "swapped, then received json"
|
||||
parse(data:json)
|
||||
|
||||
def html = """
|
||||
|
||||
Reference in New Issue
Block a user