mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-18 13:20:53 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7669bec0bc | ||
|
|
3a7abd6169 | ||
|
|
7def620f04 |
@@ -1,153 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2016 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 {
|
|
||||||
definition (name: "Arrival Sensor HA", namespace: "smartthings", author: "SmartThings") {
|
|
||||||
capability "Tone"
|
|
||||||
capability "Actuator"
|
|
||||||
capability "Presence Sensor"
|
|
||||||
capability "Sensor"
|
|
||||||
capability "Battery"
|
|
||||||
capability "Configuration"
|
|
||||||
|
|
||||||
fingerprint inClusters: "0000,0001,0003,000F,0020", outClusters: "0003,0019",
|
|
||||||
manufacturer: "SmartThings", model: "tagv4", deviceJoinName: "Arrival Sensor"
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences {
|
|
||||||
section {
|
|
||||||
image(name: 'educationalcontent', multiple: true, images: [
|
|
||||||
"http://cdn.device-gse.smartthings.com/Arrival/Arrival1.png",
|
|
||||||
"http://cdn.device-gse.smartthings.com/Arrival/Arrival2.png"
|
|
||||||
])
|
|
||||||
}
|
|
||||||
section {
|
|
||||||
input "checkInterval", "enum", title: "Presence timeout (minutes)",
|
|
||||||
defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles {
|
|
||||||
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
|
|
||||||
state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0"
|
|
||||||
state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff"
|
|
||||||
}
|
|
||||||
standardTile("beep", "device.beep", decoration: "flat") {
|
|
||||||
state "beep", label:'', action:"tone.beep", icon:"st.secondary.beep", backgroundColor:"#ffffff"
|
|
||||||
}
|
|
||||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
|
|
||||||
state "battery", label:'${currentValue}% battery', unit:""
|
|
||||||
}
|
|
||||||
|
|
||||||
main "presence"
|
|
||||||
details(["presence", "beep", "battery"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated() {
|
|
||||||
startTimer()
|
|
||||||
}
|
|
||||||
|
|
||||||
def configure() {
|
|
||||||
def cmds = zigbee.configureReporting(0x0001, 0x0020, 0x20, 20, 20, 0x01)
|
|
||||||
log.debug "configure -- cmds: ${cmds}"
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
def beep() {
|
|
||||||
log.debug "Sending Identify command to beep the sensor for 5 seconds"
|
|
||||||
return zigbee.command(0x0003, 0x00, "0500")
|
|
||||||
}
|
|
||||||
|
|
||||||
def parse(String description) {
|
|
||||||
state.lastCheckin = now()
|
|
||||||
handlePresenceEvent(true)
|
|
||||||
|
|
||||||
if (description?.startsWith('read attr -')) {
|
|
||||||
handleReportAttributeMessage(description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleReportAttributeMessage(String description) {
|
|
||||||
def descMap = zigbee.parseDescriptionAsMap(description)
|
|
||||||
|
|
||||||
if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) {
|
|
||||||
handleBatteryEvent(Integer.parseInt(descMap.value, 16))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleBatteryEvent(rawValue) {
|
|
||||||
def linkText = getLinkText(device)
|
|
||||||
|
|
||||||
def eventMap = [
|
|
||||||
name: 'battery',
|
|
||||||
value: '--'
|
|
||||||
]
|
|
||||||
|
|
||||||
def volts = rawValue / 10
|
|
||||||
if (volts > 0){
|
|
||||||
def minVolts = 2.0
|
|
||||||
def maxVolts = 2.8
|
|
||||||
|
|
||||||
if (volts < minVolts)
|
|
||||||
volts = minVolts
|
|
||||||
else if (volts > maxVolts)
|
|
||||||
volts = maxVolts
|
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
|
||||||
|
|
||||||
eventMap.value = Math.round(pct * 100)
|
|
||||||
eventMap.descriptionText = "${linkText} battery was ${eventMap.value}%"
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug "Creating battery event: ${eventMap}"
|
|
||||||
sendEvent(eventMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
private handlePresenceEvent(present) {
|
|
||||||
def wasPresent = device.currentState("presence")?.value == "present"
|
|
||||||
if (!wasPresent && present) {
|
|
||||||
log.debug "Sensor is present"
|
|
||||||
startTimer()
|
|
||||||
} else if (!present) {
|
|
||||||
log.debug "Sensor is not present"
|
|
||||||
stopTimer()
|
|
||||||
}
|
|
||||||
def linkText = getLinkText(device)
|
|
||||||
def eventMap = [
|
|
||||||
name: "presence",
|
|
||||||
value: present ? "present" : "not present",
|
|
||||||
linkText: linkText,
|
|
||||||
descriptionText: "${linkText} has ${present ? 'arrived' : 'left'}",
|
|
||||||
]
|
|
||||||
log.debug "Creating presence event: ${eventMap}"
|
|
||||||
sendEvent(eventMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
private startTimer() {
|
|
||||||
log.debug "Scheduling periodic timer"
|
|
||||||
schedule("0 * * * * ?", checkPresenceCallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
private stopTimer() {
|
|
||||||
log.debug "Stopping periodic timer"
|
|
||||||
unschedule()
|
|
||||||
}
|
|
||||||
|
|
||||||
def checkPresenceCallback() {
|
|
||||||
def timeSinceLastCheckin = (now() - state.lastCheckin) / 1000
|
|
||||||
def theCheckInterval = (checkInterval ? checkInterval as int : 2) * 60
|
|
||||||
log.debug "Sensor checked in ${timeSinceLastCheckin} seconds ago"
|
|
||||||
if (timeSinceLastCheckin >= theCheckInterval) {
|
|
||||||
handlePresenceEvent(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -67,7 +67,7 @@ metadata {
|
|||||||
state "setpoint", action:"raiseSetpoint", icon:"st.thermostat.thermostat-up"
|
state "setpoint", action:"raiseSetpoint", icon:"st.thermostat.thermostat-up"
|
||||||
}
|
}
|
||||||
valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") {
|
valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") {
|
||||||
state "thermostatSetpoint", label:'${currentValue}'
|
state "thermostatSetpoint", label:'${currentValue}°'
|
||||||
}
|
}
|
||||||
valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") {
|
valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") {
|
||||||
state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff"
|
state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff"
|
||||||
|
|||||||
@@ -204,10 +204,8 @@ private List parseReportAttributeMessage(String description) {
|
|||||||
}
|
}
|
||||||
result << getAccelerationResult(descMap.value)
|
result << getAccelerationResult(descMap.value)
|
||||||
}
|
}
|
||||||
else if (descMap.cluster == "FC02" && descMap.attrId == "0012" && descMap.value.size() == 24) {
|
else if (descMap.cluster == "FC02" && descMap.attrId == "0012") {
|
||||||
// The size is checked to ensure the attribute report contains X, Y and Z values
|
result << parseAxis(descMap.value)
|
||||||
// If all three axis are not included then the attribute report is ignored
|
|
||||||
result << parseAxis(descMap.value)
|
|
||||||
}
|
}
|
||||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
||||||
result << getBatteryResult(Integer.parseInt(descMap.value, 16))
|
result << getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||||
@@ -381,7 +379,7 @@ def getTemperature(value) {
|
|||||||
log.debug "Refreshing Values for manufacturer: SmartThings "
|
log.debug "Refreshing Values for manufacturer: SmartThings "
|
||||||
refreshCmds = refreshCmds + [
|
refreshCmds = refreshCmds + [
|
||||||
|
|
||||||
/* These values of Motion Threshold Multiplier(01) and Motion Threshold (7602)
|
/* These values of Motion Threshold Multiplier(01) and Motion Threshold (D200)
|
||||||
seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
|
seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
|
||||||
Separating these out in a separate if-else because I do not want to touch Centralite part
|
Separating these out in a separate if-else because I do not want to touch Centralite part
|
||||||
as of now.
|
as of now.
|
||||||
@@ -392,7 +390,7 @@ def getTemperature(value) {
|
|||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
|
||||||
|
|
||||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||||
"zcl global write 0xFC02 2 0x21 {7602}", "delay 200",
|
"zcl global write 0xFC02 2 0x21 {D200}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
|
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
|
||||||
|
|
||||||
]
|
]
|
||||||
@@ -478,34 +476,50 @@ def enrollResponse() {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Map parseAxis(String description) {
|
private Map parseAxis(String description) {
|
||||||
def hexToSignedInt = { hexVal ->
|
log.debug "parseAxis"
|
||||||
def unsignedVal = hexToInt(hexVal)
|
def xyzResults = [x: 0, y: 0, z: 0]
|
||||||
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
def parts = description.split("2900")
|
||||||
}
|
parts[0] = "12" + parts[0]
|
||||||
|
parts.each { part ->
|
||||||
def z = hexToSignedInt(description[0..3])
|
part = part.trim()
|
||||||
def y = hexToSignedInt(description[10..13])
|
if (part.startsWith("12")) {
|
||||||
def x = hexToSignedInt(description[20..23])
|
def unsignedX = hexToInt(part.split("12")[1].trim())
|
||||||
def xyzResults = [x: x, y: y, z: z]
|
def signedX = unsignedX > 32767 ? unsignedX - 65536 : unsignedX
|
||||||
|
xyzResults.x = signedX
|
||||||
if (device.getDataValue("manufacturer") == "SmartThings") {
|
log.debug "X Part: ${signedX}"
|
||||||
// This mapping matches the current behavior of the Device Handler for the Centralite sensors
|
}
|
||||||
xyzResults.x = z
|
// Y and the Z axes are interchanged between SmartThings's implementation and Centralite's implementation
|
||||||
xyzResults.y = y
|
else if (part.startsWith("13")) {
|
||||||
xyzResults.z = -x
|
def unsignedY = hexToInt(part.split("13")[1].trim())
|
||||||
} else {
|
def signedY = unsignedY > 32767 ? unsignedY - 65536 : unsignedY
|
||||||
// The axises reported by the Device Handler differ from the axises reported by the sensor
|
if (device.getDataValue("manufacturer") == "SmartThings") {
|
||||||
// This may change in the future
|
xyzResults.z = -signedY
|
||||||
xyzResults.x = z
|
log.debug "Z Part: ${xyzResults.z}"
|
||||||
xyzResults.y = x
|
if (garageSensor == "Yes")
|
||||||
xyzResults.z = y
|
garageEvent(xyzResults.z)
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
log.debug "parseAxis -- ${xyzResults}"
|
xyzResults.y = signedY
|
||||||
|
log.debug "Y Part: ${signedY}"
|
||||||
if (garageSensor == "Yes")
|
}
|
||||||
garageEvent(xyzResults.z)
|
}
|
||||||
|
else if (part.startsWith("14")) {
|
||||||
|
def unsignedZ = hexToInt(part.split("14")[1].trim())
|
||||||
|
def signedZ = unsignedZ > 32767 ? unsignedZ - 65536 : unsignedZ
|
||||||
|
if (device.getDataValue("manufacturer") == "SmartThings") {
|
||||||
|
xyzResults.y = signedZ
|
||||||
|
log.debug "Y Part: ${signedZ}"
|
||||||
|
} else {
|
||||||
|
xyzResults.z = signedZ
|
||||||
|
log.debug "Z Part: ${signedZ}"
|
||||||
|
if (garageSensor == "Yes")
|
||||||
|
garageEvent(signedZ)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getXyzResult(xyzResults, description)
|
getXyzResult(xyzResults, description)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2015 SmartThings
|
* Copyright 2014 SmartThings
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
* 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:
|
* in compliance with the License. You may obtain a copy of the License at:
|
||||||
@@ -15,7 +15,6 @@ metadata {
|
|||||||
// Automatically generated. Make future change here.
|
// Automatically generated. Make future change here.
|
||||||
definition (name: "Simulated Thermostat", namespace: "smartthings/testing", author: "SmartThings") {
|
definition (name: "Simulated Thermostat", namespace: "smartthings/testing", author: "SmartThings") {
|
||||||
capability "Thermostat"
|
capability "Thermostat"
|
||||||
capability "Relative Humidity Measurement"
|
|
||||||
|
|
||||||
command "tempUp"
|
command "tempUp"
|
||||||
command "tempDown"
|
command "tempDown"
|
||||||
@@ -23,40 +22,11 @@ metadata {
|
|||||||
command "heatDown"
|
command "heatDown"
|
||||||
command "coolUp"
|
command "coolUp"
|
||||||
command "coolDown"
|
command "coolDown"
|
||||||
command "setTemperature", ["number"]
|
command "setTemperature", ["number"]
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
tiles {
|
||||||
multiAttributeTile(name:"thermostatMulti", type:"thermostat", width:6, height:4) {
|
valueTile("temperature", "device.temperature", width: 1, height: 1) {
|
||||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
|
||||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
|
||||||
}
|
|
||||||
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
|
|
||||||
attributeState("default", action: "setTemperature")
|
|
||||||
}
|
|
||||||
tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
|
|
||||||
attributeState("default", label:'${currentValue}%', unit:"%")
|
|
||||||
}
|
|
||||||
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
|
|
||||||
attributeState("idle", backgroundColor:"#44b621")
|
|
||||||
attributeState("heating", backgroundColor:"#ffa81e")
|
|
||||||
attributeState("cooling", backgroundColor:"#269bd2")
|
|
||||||
}
|
|
||||||
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
|
|
||||||
attributeState("off", label:'${name}')
|
|
||||||
attributeState("heat", label:'${name}')
|
|
||||||
attributeState("cool", label:'${name}')
|
|
||||||
attributeState("auto", label:'${name}')
|
|
||||||
}
|
|
||||||
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
|
|
||||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
|
||||||
}
|
|
||||||
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
|
|
||||||
attributeState("default", label:'${currentValue}', unit:"dF")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
|
||||||
state("temperature", label:'${currentValue}', unit:"dF",
|
state("temperature", label:'${currentValue}', unit:"dF",
|
||||||
backgroundColors:[
|
backgroundColors:[
|
||||||
[value: 31, color: "#153591"],
|
[value: 31, color: "#153591"],
|
||||||
@@ -69,51 +39,51 @@ metadata {
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
standardTile("tempDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("tempDown", "device.temperature", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", label:'down', action:"tempDown"
|
state "default", label:'down', action:"tempDown"
|
||||||
}
|
}
|
||||||
standardTile("tempUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("tempUp", "device.temperature", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", label:'up', action:"tempUp"
|
state "default", label:'up', action:"tempUp"
|
||||||
}
|
}
|
||||||
|
|
||||||
valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||||
state "heat", label:'${currentValue} heat', unit: "F", backgroundColor:"#ffffff"
|
state "heat", label:'${currentValue} heat', unit: "F", backgroundColor:"#ffffff"
|
||||||
}
|
}
|
||||||
standardTile("heatDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("heatDown", "device.temperature", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", label:'down', action:"heatDown"
|
state "default", label:'down', action:"heatDown"
|
||||||
}
|
}
|
||||||
standardTile("heatUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("heatUp", "device.temperature", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", label:'up', action:"heatUp"
|
state "default", label:'up', action:"heatUp"
|
||||||
}
|
}
|
||||||
|
|
||||||
valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||||
state "cool", label:'${currentValue} cool', unit:"F", backgroundColor:"#ffffff"
|
state "cool", label:'${currentValue} cool', unit:"F", backgroundColor:"#ffffff"
|
||||||
}
|
}
|
||||||
standardTile("coolDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("coolDown", "device.temperature", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", label:'down', action:"coolDown"
|
state "default", label:'down', action:"coolDown"
|
||||||
}
|
}
|
||||||
standardTile("coolUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("coolUp", "device.temperature", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", label:'up', action:"coolUp"
|
state "default", label:'up', action:"coolUp"
|
||||||
}
|
}
|
||||||
|
|
||||||
standardTile("mode", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||||
state "off", label:'${name}', action:"thermostat.heat", backgroundColor:"#ffffff"
|
state "off", label:'${name}', action:"thermostat.heat", backgroundColor:"#ffffff"
|
||||||
state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#ffa81e"
|
state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#ffa81e"
|
||||||
state "cool", label:'${name}', action:"thermostat.auto", backgroundColor:"#269bd2"
|
state "cool", label:'${name}', action:"thermostat.auto", backgroundColor:"#269bd2"
|
||||||
state "auto", label:'${name}', action:"thermostat.off", backgroundColor:"#79b821"
|
state "auto", label:'${name}', action:"thermostat.off", backgroundColor:"#79b821"
|
||||||
}
|
}
|
||||||
standardTile("fanMode", "device.thermostatFanMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
|
||||||
state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff"
|
state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff"
|
||||||
state "fanOn", label:'${name}', action:"thermostat.fanCirculate", backgroundColor:"#ffffff"
|
state "fanOn", label:'${name}', action:"thermostat.fanCirculate", backgroundColor:"#ffffff"
|
||||||
state "fanCirculate", label:'${name}', action:"thermostat.fanAuto", backgroundColor:"#ffffff"
|
state "fanCirculate", label:'${name}', action:"thermostat.fanAuto", backgroundColor:"#ffffff"
|
||||||
}
|
}
|
||||||
standardTile("operatingState", "device.thermostatOperatingState", width: 2, height: 2) {
|
standardTile("operatingState", "device.thermostatOperatingState") {
|
||||||
state "idle", label:'${name}', backgroundColor:"#ffffff"
|
state "idle", label:'${name}', backgroundColor:"#ffffff"
|
||||||
state "heating", label:'${name}', backgroundColor:"#ffa81e"
|
state "heating", label:'${name}', backgroundColor:"#ffa81e"
|
||||||
state "cooling", label:'${name}', backgroundColor:"#269bd2"
|
state "cooling", label:'${name}', backgroundColor:"#269bd2"
|
||||||
}
|
}
|
||||||
|
|
||||||
main("thermostatMulti")
|
main("temperature","operatingState")
|
||||||
details([
|
details([
|
||||||
"temperature","tempDown","tempUp",
|
"temperature","tempDown","tempUp",
|
||||||
"mode", "fanMode", "operatingState",
|
"mode", "fanMode", "operatingState",
|
||||||
@@ -131,7 +101,6 @@ def installed() {
|
|||||||
sendEvent(name: "thermostatMode", value: "off")
|
sendEvent(name: "thermostatMode", value: "off")
|
||||||
sendEvent(name: "thermostatFanMode", value: "fanAuto")
|
sendEvent(name: "thermostatFanMode", value: "fanAuto")
|
||||||
sendEvent(name: "thermostatOperatingState", value: "idle")
|
sendEvent(name: "thermostatOperatingState", value: "idle")
|
||||||
sendEvent(name: "humidity", value: 53, unit: "%")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ metadata {
|
|||||||
|
|
||||||
tiles {
|
tiles {
|
||||||
|
|
||||||
valueTile("power", "device.power", canChangeIcon: true) {
|
valueTile("power", "device.power") {
|
||||||
state "power", label: '${currentValue} W'
|
state "power", label: '${currentValue} W'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ User-Agent: CyberGarage-HTTP/1.0
|
|||||||
def poll() {
|
def poll() {
|
||||||
log.debug "Executing 'poll'"
|
log.debug "Executing 'poll'"
|
||||||
if (device.currentValue("currentIP") != "Offline")
|
if (device.currentValue("currentIP") != "Offline")
|
||||||
runIn(30, setOffline)
|
runIn(10, setOffline)
|
||||||
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
|
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
|
||||||
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
|
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
|
||||||
Content-Length: 277
|
Content-Length: 277
|
||||||
|
|||||||
@@ -77,8 +77,9 @@ def parse(String description) {
|
|||||||
def result = []
|
def result = []
|
||||||
def bodyString = msg.body
|
def bodyString = msg.body
|
||||||
if (bodyString) {
|
if (bodyString) {
|
||||||
unschedule("setOffline")
|
unschedule("setOffline")
|
||||||
def body = new XmlSlurper().parseText(bodyString)
|
def body = new XmlSlurper().parseText(bodyString)
|
||||||
|
|
||||||
if (body?.property?.TimeSyncRequest?.text()) {
|
if (body?.property?.TimeSyncRequest?.text()) {
|
||||||
log.trace "Got TimeSyncRequest"
|
log.trace "Got TimeSyncRequest"
|
||||||
result << timeSyncResponse()
|
result << timeSyncResponse()
|
||||||
@@ -133,7 +134,7 @@ def refresh() {
|
|||||||
def getStatus() {
|
def getStatus() {
|
||||||
log.debug "Executing WeMo Motion 'getStatus'"
|
log.debug "Executing WeMo Motion 'getStatus'"
|
||||||
if (device.currentValue("currentIP") != "Offline")
|
if (device.currentValue("currentIP") != "Offline")
|
||||||
runIn(30, setOffline)
|
runIn(10, setOffline)
|
||||||
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
|
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
|
||||||
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
|
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
|
||||||
Content-Length: 277
|
Content-Length: 277
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
command "subscribe"
|
command "subscribe"
|
||||||
command "resubscribe"
|
command "resubscribe"
|
||||||
command "unsubscribe"
|
command "unsubscribe"
|
||||||
command "setOffline"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulator metadata
|
// simulator metadata
|
||||||
@@ -208,7 +207,7 @@ def subscribe(ip, port) {
|
|||||||
def existingIp = getDataValue("ip")
|
def existingIp = getDataValue("ip")
|
||||||
def existingPort = getDataValue("port")
|
def existingPort = getDataValue("port")
|
||||||
if (ip && ip != existingIp) {
|
if (ip && ip != existingIp) {
|
||||||
log.debug "Updating ip from $existingIp to $ip"
|
log.debug "Updating ip from $existingIp to $ip"
|
||||||
updateDataValue("ip", ip)
|
updateDataValue("ip", ip)
|
||||||
def ipvalue = convertHexToIP(getDataValue("ip"))
|
def ipvalue = convertHexToIP(getDataValue("ip"))
|
||||||
sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
|
sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
|
||||||
@@ -276,7 +275,7 @@ def setOffline() {
|
|||||||
def poll() {
|
def poll() {
|
||||||
log.debug "Executing 'poll'"
|
log.debug "Executing 'poll'"
|
||||||
if (device.currentValue("currentIP") != "Offline")
|
if (device.currentValue("currentIP") != "Offline")
|
||||||
runIn(30, setOffline)
|
runIn(10, setOffline)
|
||||||
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
|
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
|
||||||
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
|
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
|
||||||
Content-Length: 277
|
Content-Length: 277
|
||||||
@@ -291,4 +290,4 @@ User-Agent: CyberGarage-HTTP/1.0
|
|||||||
</u:GetBinaryState>
|
</u:GetBinaryState>
|
||||||
</s:Body>
|
</s:Body>
|
||||||
</s:Envelope>""", physicalgraph.device.Protocol.LAN)
|
</s:Envelope>""", physicalgraph.device.Protocol.LAN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,243 @@
|
|||||||
|
metadata {
|
||||||
|
definition (name: "Timevalve Smart", namespace: "timevalve.gaslock.t-08", author: "ruinnel") {
|
||||||
|
capability "Valve"
|
||||||
|
capability "Refresh"
|
||||||
|
capability "Battery"
|
||||||
|
capability "Temperature Measurement"
|
||||||
|
|
||||||
|
command "setRemaining"
|
||||||
|
command "setTimeout"
|
||||||
|
command "setTimeout10"
|
||||||
|
command "setTimeout20"
|
||||||
|
command "setTimeout30"
|
||||||
|
command "setTimeout40"
|
||||||
|
|
||||||
|
command "remainingLevel"
|
||||||
|
|
||||||
|
attribute "remaining", "number"
|
||||||
|
attribute "remainingText", "String"
|
||||||
|
attribute "timeout", "number"
|
||||||
|
|
||||||
|
//raw desc : 0 0 0x1006 0 0 0 7 0x5E 0x86 0x72 0x5A 0x73 0x98 0x80
|
||||||
|
//fingerprint deviceId:"0x1006", inClusters:"0x5E, 0x86, 0x72, 0x5A, 0x73, 0x98, 0x80"
|
||||||
|
}
|
||||||
|
|
||||||
|
tiles (scale: 2) {
|
||||||
|
multiAttributeTile(name:"statusTile", type:"generic", width:6, height:4) {
|
||||||
|
tileAttribute("device.contact", key: "PRIMARY_CONTROL") {
|
||||||
|
attributeState "open", label: '${name}', action: "close", icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
|
||||||
|
attributeState "closed", label:'${name}', action: "", icon:"st.contact.contact.closed", backgroundColor:"#79b821"
|
||||||
|
}
|
||||||
|
tileAttribute("device.remainingText", key: "SECONDARY_CONTROL") {
|
||||||
|
attributeState "open", label: '${currentValue}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
|
||||||
|
attributeState "closed", label:'', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
standardTile("refreshTile", "command.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
|
}
|
||||||
|
|
||||||
|
controlTile("remainingSliderTile", "device.remaining", "slider", inactiveLabel: false, range:"(0..590)", height: 2, width: 4) {
|
||||||
|
state "level", action:"remainingLevel"
|
||||||
|
}
|
||||||
|
valueTile("setRemaining", "device.remainingText", inactiveLabel: false, decoration: "flat", height: 2, width: 2){
|
||||||
|
state "remainingText", label:'${currentValue}\nRemaining'//, action: "setRemaining"//, icon: "st.Office.office6"
|
||||||
|
}
|
||||||
|
|
||||||
|
standardTile("setTimeout10", "device.remaining", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "default", label:'10Min', action: "setTimeout10", icon:"st.Health & Wellness.health7", defaultState: true
|
||||||
|
state "10", label:'10Min', action: "setTimeout10", icon:"st.Office.office13"
|
||||||
|
}
|
||||||
|
standardTile("setTimeout20", "device.remaining", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "default", label:'20Min', action: "setTimeout20", icon:"st.Health & Wellness.health7", defaultState: true
|
||||||
|
state "20", label:'20Min', action: "setTimeout20", icon:"st.Office.office13"
|
||||||
|
}
|
||||||
|
standardTile("setTimeout30", "device.remaining", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "default", label:'30Min', action: "setTimeout30", icon:"st.Health & Wellness.health7", defaultState: true
|
||||||
|
state "30", label:'30Min', action: "setTimeout30", icon:"st.Office.office13"
|
||||||
|
}
|
||||||
|
standardTile("setTimeout40", "device.remaining", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "default", label:'40Min', action: "setTimeout40", icon:"st.Health & Wellness.health7", defaultState: true
|
||||||
|
state "40", label:'40Min', action: "setTimeout40", icon:"st.Office.office13"
|
||||||
|
}
|
||||||
|
|
||||||
|
valueTile("batteryTile", "device.battery", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "battery", label:'${currentValue}% battery', unit:""
|
||||||
|
}
|
||||||
|
|
||||||
|
main (["statusTile"])
|
||||||
|
// details (["statusTile", "remainingSliderTile", "setRemaining", "setTimeout10", "setTimeout20", "batteryTile", "refreshTile", "setTimeout30", "setTimeout40"])
|
||||||
|
// details (["statusTile", "batteryTile", "setRemaining", "refreshTile"])
|
||||||
|
details (["statusTile", "batteryTile", "refreshTile"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse(description) {
|
||||||
|
// log.debug "parse - " + description
|
||||||
|
def result = null
|
||||||
|
if (description.startsWith("Err 106")) {
|
||||||
|
state.sec = 0
|
||||||
|
result = createEvent(descriptionText: description, isStateChange: true)
|
||||||
|
} else if (description != "updated") {
|
||||||
|
def cmd = zwave.parse(description, [0x20: 1, 0x25: 1, 0x70: 1, 0x71: 1, 0x98: 1])
|
||||||
|
if (cmd) {
|
||||||
|
log.debug "parsed cmd = " + cmd
|
||||||
|
result = zwaveEvent(cmd)
|
||||||
|
//log.debug("'$description' parsed to $result")
|
||||||
|
} else {
|
||||||
|
log.debug("Couldn't zwave.parse '$description'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 복호화 후 zwaveEvent() 호출
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
||||||
|
//log.debug "SecurityMessageEncapsulation - " + cmd
|
||||||
|
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 1, 0x70: 1, 0x71: 1, 0x98: 1])
|
||||||
|
if (encapsulatedCommand) {
|
||||||
|
state.sec = 1
|
||||||
|
log.debug "encapsulatedCommand = " + encapsulatedCommand
|
||||||
|
zwaveEvent(encapsulatedCommand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
|
||||||
|
//log.debug "switch status - " + cmd.value
|
||||||
|
createEvent(name:"contact", value: cmd.value ? "open" : "closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
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 - ${map.value}${map.unit}"
|
||||||
|
// Store time of last battery update so we don't ask every wakeup, see WakeUpNotification handler
|
||||||
|
state.lastbatt = new Date().time
|
||||||
|
createEvent(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||||
|
//log.debug "zwaveEvent - ${device.displayName}: ${cmd}"
|
||||||
|
createEvent(descriptionText: "${device.displayName}: ${cmd}")
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
|
||||||
|
def result = []
|
||||||
|
log.info "zwave.configurationV1.configurationGet - " + cmd
|
||||||
|
def array = cmd.configurationValue
|
||||||
|
def value = ( (array[0] * 0x1000000) + (array[1] * 0x10000) + (array[2] * 0x100) + array[3] ).intdiv(60)
|
||||||
|
if (device.currentValue("contact") == "open") {
|
||||||
|
value = ( (array[0] * 0x1000000) + (array[1] * 0x10000) + (array[2] * 0x100) + array[3] ).intdiv(60)
|
||||||
|
} else {
|
||||||
|
value = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device.currentValue('contact') == 'open') {
|
||||||
|
def hour = value.intdiv(60);
|
||||||
|
def min = (value % 60).toString().padLeft(2, '0');
|
||||||
|
def text = "${hour}:${min}M"
|
||||||
|
|
||||||
|
log.info "remain - " + text
|
||||||
|
result.add( createEvent(name: "remaining", value: value, displayed: false, isStateChange: true) )
|
||||||
|
result.add( createEvent(name: "remainingText", value: text, displayed: false, isStateChange: true) )
|
||||||
|
} else {
|
||||||
|
result.add( createEvent(name: "timeout", value: value, displayed: false, isStateChange: true) )
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
||||||
|
def type = cmd.notificationType
|
||||||
|
if (type == cmd.NOTIFICATION_TYPE_HEAT) {
|
||||||
|
log.info "NotificationReport - ${type}"
|
||||||
|
createEvent(name: "temperature", value: 999, unit: "C", descriptionText: "${device.displayName} is over heat!", displayed: true, isStateChange: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) {
|
||||||
|
def type = cmd.alarmType
|
||||||
|
def level = cmd.alarmLevel
|
||||||
|
|
||||||
|
log.info "AlarmReport - type : ${type}, level : ${level}"
|
||||||
|
def msg = "${device.displayName} is over heat!"
|
||||||
|
def result = createEvent(name: "temperature", value: 999, unit: "C", descriptionText: msg, displayed: true, isStateChange: true)
|
||||||
|
if (sendPushMessage) {
|
||||||
|
sendPushMessage(msg)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// remote open not allow
|
||||||
|
def open() {}
|
||||||
|
|
||||||
|
def close() {
|
||||||
|
// log.debug 'cmd - close()'
|
||||||
|
commands([
|
||||||
|
zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00),
|
||||||
|
zwave.switchBinaryV1.switchBinaryGet()
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
def setTimeout10() { setTimeout(10) }
|
||||||
|
def setTimeout20() { setTimeout(20) }
|
||||||
|
def setTimeout30() { setTimeout(30) }
|
||||||
|
def setTimeout40() { setTimeout(40) }
|
||||||
|
|
||||||
|
|
||||||
|
def setTimeout(value) {
|
||||||
|
// log.debug "setDefaultTime($value)"
|
||||||
|
commands([
|
||||||
|
zwave.configurationV1.configurationSet(parameterNumber: 0x01, size: 4, scaledConfigurationValue: value * 60),
|
||||||
|
zwave.configurationV1.configurationGet(parameterNumber: 0x01)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
def remainingLevel(value) {
|
||||||
|
// log.debug "remainingLevel($value)"
|
||||||
|
def hour = value.intdiv(60);
|
||||||
|
def min = (value % 60).toString().padLeft(2, '0');
|
||||||
|
def text = "${hour}:${min}M"
|
||||||
|
sendEvent(name: "remaining", value: value, displayed: false, isStateChange: true)
|
||||||
|
sendEvent(name: "remainingText", value: text, displayed: false, isStateChange: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
def setRemaining() {
|
||||||
|
def remaining = device.currentValue("remaining")
|
||||||
|
// log.debug "setConfiguration() - remaining : $remaining"
|
||||||
|
commands([
|
||||||
|
zwave.configurationV1.configurationSet(parameterNumber: 0x03, size: 4, scaledConfigurationValue: remaining * 60),
|
||||||
|
zwave.configurationV1.configurationGet(parameterNumber: 0x03)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private command(physicalgraph.zwave.Command cmd) {
|
||||||
|
if (state.sec != 0 && !(cmd instanceof physicalgraph.zwave.commands.batteryv1.BatteryGet)) {
|
||||||
|
log.debug "cmd = " + cmd + ", encapsulation"
|
||||||
|
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
||||||
|
} else {
|
||||||
|
log.debug "cmd = " + cmd + ", plain"
|
||||||
|
cmd.format()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private commands(commands, delay=200) {
|
||||||
|
delayBetween(commands.collect{ command(it) }, delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
def refresh() {
|
||||||
|
// log.debug 'cmd - refresh()'
|
||||||
|
commands([
|
||||||
|
zwave.batteryV1.batteryGet(),
|
||||||
|
zwave.switchBinaryV1.switchBinaryGet(),
|
||||||
|
zwave.configurationV1.configurationGet(parameterNumber: 0x01),
|
||||||
|
zwave.configurationV1.configurationGet(parameterNumber: 0x03)
|
||||||
|
], 400)
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB |
@@ -1,189 +0,0 @@
|
|||||||
/**
|
|
||||||
* Vinli Home Beta
|
|
||||||
*
|
|
||||||
* Copyright 2015 Daniel
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
definition(
|
|
||||||
name: "Vinli Home Connect",
|
|
||||||
namespace: "com.vinli.smartthings",
|
|
||||||
author: "Daniel",
|
|
||||||
description: "Allows Vinli users to connect their car to SmartThings",
|
|
||||||
category: "SmartThings Labs",
|
|
||||||
iconUrl: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_60.png",
|
|
||||||
iconX2Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_120.png",
|
|
||||||
iconX3Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_120.png",
|
|
||||||
oauth: true)
|
|
||||||
|
|
||||||
preferences {
|
|
||||||
section ("Allow external service to control these things...") {
|
|
||||||
input "switches", "capability.switch", multiple: true, required: true
|
|
||||||
input "locks", "capability.lock", multiple: true, required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mappings {
|
|
||||||
|
|
||||||
path("/devices") {
|
|
||||||
action: [
|
|
||||||
GET: "listAllDevices"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
path("/switches") {
|
|
||||||
action: [
|
|
||||||
GET: "listSwitches"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
path("/switches/:command") {
|
|
||||||
action: [
|
|
||||||
PUT: "updateSwitches"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
path("/switches/:id/:command") {
|
|
||||||
action: [
|
|
||||||
PUT: "updateSwitch"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
path("/locks/:command") {
|
|
||||||
action: [
|
|
||||||
PUT: "updateLocks"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
path("/locks/:id/:command") {
|
|
||||||
action: [
|
|
||||||
PUT: "updateLock"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
path("/devices/:id/:command") {
|
|
||||||
action: [
|
|
||||||
PUT: "commandDevice"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns a list of all devices
|
|
||||||
def listAllDevices() {
|
|
||||||
def resp = []
|
|
||||||
switches.each {
|
|
||||||
resp << [name: it.name, label: it.label, value: it.currentValue("switch"), type: "switch", id: it.id, hub: it.hub.name]
|
|
||||||
}
|
|
||||||
|
|
||||||
locks.each {
|
|
||||||
resp << [name: it.name, label: it.label, value: it.currentValue("lock"), type: "lock", id: it.id, hub: it.hub.name]
|
|
||||||
}
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns a list like
|
|
||||||
// [[name: "kitchen lamp", value: "off"], [name: "bathroom", value: "on"]]
|
|
||||||
def listSwitches() {
|
|
||||||
def resp = []
|
|
||||||
switches.each {
|
|
||||||
resp << [name: it.displayName, value: it.currentValue("switch"), type: "switch", id: it.id]
|
|
||||||
}
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateLocks() {
|
|
||||||
// use the built-in request object to get the command parameter
|
|
||||||
def command = params.command
|
|
||||||
|
|
||||||
if (command) {
|
|
||||||
|
|
||||||
// check that the switch supports the specified command
|
|
||||||
// If not, return an error using httpError, providing a HTTP status code.
|
|
||||||
locks.each {
|
|
||||||
if (!it.hasCommand(command)) {
|
|
||||||
httpError(501, "$command is not a valid command for all switches specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// all switches have the comand
|
|
||||||
// execute the command on all switches
|
|
||||||
// (note we can do this on the array - the command will be invoked on every element
|
|
||||||
locks."$command"()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateLock() {
|
|
||||||
def command = params.command
|
|
||||||
|
|
||||||
locks.each {
|
|
||||||
if (!it.hasCommand(command)) {
|
|
||||||
httpError(400, "$command is not a valid command for all lock specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it.id == params.id) {
|
|
||||||
it."$command"()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSwitch() {
|
|
||||||
def command = params.command
|
|
||||||
|
|
||||||
switches.each {
|
|
||||||
if (!it.hasCommand(command)) {
|
|
||||||
httpError(400, "$command is not a valid command for all switches specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it.id == params.id) {
|
|
||||||
it."$command"()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void commandDevice() {
|
|
||||||
def command = params.command
|
|
||||||
def devices = []
|
|
||||||
|
|
||||||
switches.each {
|
|
||||||
devices << it
|
|
||||||
}
|
|
||||||
|
|
||||||
locks.each {
|
|
||||||
devices << it
|
|
||||||
}
|
|
||||||
|
|
||||||
devices.each {
|
|
||||||
if (it.id == params.id) {
|
|
||||||
if (!it.hasCommand(command)) {
|
|
||||||
httpError(400, "$command is not a valid command for specified device")
|
|
||||||
}
|
|
||||||
it."$command"()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSwitches() {
|
|
||||||
// use the built-in request object to get the command parameter
|
|
||||||
def command = params.command
|
|
||||||
|
|
||||||
if (command) {
|
|
||||||
|
|
||||||
// check that the switch supports the specified command
|
|
||||||
// If not, return an error using httpError, providing a HTTP status code.
|
|
||||||
switches.each {
|
|
||||||
if (!it.hasCommand(command)) {
|
|
||||||
httpError(400, "$command is not a valid command for all switches specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// all switches have the comand
|
|
||||||
// execute the command on all switches
|
|
||||||
// (note we can do this on the array - the command will be invoked on every element
|
|
||||||
switches."$command"()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def installed() {
|
|
||||||
log.debug "Installed with settings: ${settings}"
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated() {
|
|
||||||
log.debug "Updated with settings: ${settings}"
|
|
||||||
|
|
||||||
unsubscribe()
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ preferences{
|
|||||||
input "lock1", "capability.lock", required: true
|
input "lock1", "capability.lock", required: true
|
||||||
}
|
}
|
||||||
section("Select the door contact sensor:") {
|
section("Select the door contact sensor:") {
|
||||||
input "contact", "capability.contactSensor", required: true
|
input "contact", "capability.contactSensor", required: true
|
||||||
}
|
}
|
||||||
section("Automatically lock the door when closed...") {
|
section("Automatically lock the door when closed...") {
|
||||||
input "minutesLater", "number", title: "Delay (in minutes):", required: true
|
input "minutesLater", "number", title: "Delay (in minutes):", required: true
|
||||||
@@ -22,10 +22,9 @@ preferences{
|
|||||||
input "secondsLater", "number", title: "Delay (in seconds):", required: true
|
input "secondsLater", "number", title: "Delay (in seconds):", required: true
|
||||||
}
|
}
|
||||||
section( "Notifications" ) {
|
section( "Notifications" ) {
|
||||||
input("recipients", "contact", title: "Send notifications to", required: false) {
|
input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes", "No"]], required: false
|
||||||
input "phoneNumber", "phone", title: "Warn with text message (optional)", description: "Phone Number", required: false
|
input "phoneNumber", "phone", title: "Enter phone number to send text notification.", required: false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def installed(){
|
def installed(){
|
||||||
@@ -43,73 +42,55 @@ def initialize(){
|
|||||||
subscribe(lock1, "lock", doorHandler, [filterEvents: false])
|
subscribe(lock1, "lock", doorHandler, [filterEvents: false])
|
||||||
subscribe(lock1, "unlock", doorHandler, [filterEvents: false])
|
subscribe(lock1, "unlock", doorHandler, [filterEvents: false])
|
||||||
subscribe(contact, "contact.open", doorHandler)
|
subscribe(contact, "contact.open", doorHandler)
|
||||||
subscribe(contact, "contact.closed", doorHandler)
|
subscribe(contact, "contact.closed", doorHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
def lockDoor(){
|
def lockDoor(){
|
||||||
log.debug "Locking the door."
|
log.debug "Locking the door."
|
||||||
lock1.lock()
|
lock1.lock()
|
||||||
if(location.contactBookEnabled) {
|
log.debug ( "Sending Push Notification..." )
|
||||||
if ( recipients ) {
|
if ( sendPushMessage != "No" ) sendPush( "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!" )
|
||||||
log.debug ( "Sending Push Notification..." )
|
log.debug("Sending text message...")
|
||||||
sendNotificationToContacts( "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!", recipients)
|
if ( phoneNumber != "0" ) sendSms( phoneNumber, "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!" )
|
||||||
}
|
|
||||||
}
|
|
||||||
if (phoneNumber) {
|
|
||||||
log.debug("Sending text message...")
|
|
||||||
sendSms( phoneNumber, "${lock1} locked after ${contact} was closed for ${minutesLater} minutes!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def unlockDoor(){
|
def unlockDoor(){
|
||||||
log.debug "Unlocking the door."
|
log.debug "Unlocking the door."
|
||||||
lock1.unlock()
|
lock1.unlock()
|
||||||
if(location.contactBookEnabled) {
|
log.debug ( "Sending Push Notification..." )
|
||||||
if ( recipients ) {
|
if ( sendPushMessage != "No" ) sendPush( "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!" )
|
||||||
log.debug ( "Sending Push Notification..." )
|
log.debug("Sending text message...")
|
||||||
sendNotificationToContacts( "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!", recipients)
|
if ( phoneNumber != "0" ) sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!" )
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( phoneNumber ) {
|
|
||||||
log.debug("Sending text message...")
|
|
||||||
sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened for ${secondsLater} seconds!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def doorHandler(evt){
|
def doorHandler(evt){
|
||||||
if ((contact.latestValue("contact") == "open") && (evt.value == "locked")) { // If the door is open and a person locks the door then...
|
if ((contact.latestValue("contact") == "open") && (evt.value == "locked")) { // If the door is open and a person locks the door then...
|
||||||
//def delay = (secondsLater) // runIn uses seconds
|
def delay = (secondsLater) // runIn uses seconds
|
||||||
runIn( secondsLater, unlockDoor ) // ...schedule (in minutes) to unlock... We don't want the door to be closed while the lock is engaged.
|
runIn( delay, unlockDoor ) // ...schedule (in minutes) to unlock... We don't want the door to be closed while the lock is engaged.
|
||||||
}
|
}
|
||||||
else if ((contact.latestValue("contact") == "open") && (evt.value == "unlocked")) { // If the door is open and a person unlocks it then...
|
else if ((contact.latestValue("contact") == "open") && (evt.value == "unlocked")) { // If the door is open and a person unlocks it then...
|
||||||
unschedule( unlockDoor ) // ...we don't need to unlock it later.
|
unschedule( unlockDoor ) // ...we don't need to unlock it later.
|
||||||
}
|
}
|
||||||
else if ((contact.latestValue("contact") == "closed") && (evt.value == "locked")) { // If the door is closed and a person manually locks it then...
|
else if ((contact.latestValue("contact") == "closed") && (evt.value == "locked")) { // If the door is closed and a person manually locks it then...
|
||||||
unschedule( lockDoor ) // ...we don't need to lock it later.
|
unschedule( lockDoor ) // ...we don't need to lock it later.
|
||||||
}
|
}
|
||||||
else if ((contact.latestValue("contact") == "closed") && (evt.value == "unlocked")) { // If the door is closed and a person unlocks it then...
|
else if ((contact.latestValue("contact") == "closed") && (evt.value == "unlocked")) { // If the door is closed and a person unlocks it then...
|
||||||
//def delay = (minutesLater * 60) // runIn uses seconds
|
def delay = (minutesLater * 60) // runIn uses seconds
|
||||||
runIn( (minutesLater * 60), lockDoor ) // ...schedule (in minutes) to lock.
|
runIn( delay, lockDoor ) // ...schedule (in minutes) to lock.
|
||||||
}
|
}
|
||||||
else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "open")) { // If a person opens an unlocked door...
|
else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "open")) { // If a person opens an unlocked door...
|
||||||
unschedule( lockDoor ) // ...we don't need to lock it later.
|
unschedule( lockDoor ) // ...we don't need to lock it later.
|
||||||
}
|
}
|
||||||
else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "closed")) { // If a person closes an unlocked door...
|
else if ((lock1.latestValue("lock") == "unlocked") && (evt.value == "closed")) { // If a person closes an unlocked door...
|
||||||
//def delay = (minutesLater * 60) // runIn uses seconds
|
def delay = (minutesLater * 60) // runIn uses seconds
|
||||||
runIn( (minutesLater * 60), lockDoor ) // ...schedule (in minutes) to lock.
|
runIn( delay, lockDoor ) // ...schedule (in minutes) to lock.
|
||||||
}
|
}
|
||||||
else { //Opening or Closing door when locked (in case you have a handle lock)
|
else { //Opening or Closing door when locked (in case you have a handle lock)
|
||||||
log.debug "Unlocking the door."
|
log.debug "Unlocking the door."
|
||||||
lock1.unlock()
|
lock1.unlock()
|
||||||
if(location.contactBookEnabled) {
|
log.debug ( "Sending Push Notification..." )
|
||||||
if ( recipients ) {
|
if ( sendPushMessage != "No" ) sendPush( "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!" )
|
||||||
log.debug ( "Sending Push Notification..." )
|
log.debug("Sending text message...")
|
||||||
sendNotificationToContacts( "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!", recipients)
|
if ( phoneNumber != "0" ) sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!" )
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ( phoneNumber ) {
|
|
||||||
log.debug("Sending text message...")
|
|
||||||
sendSms( phoneNumber, "${lock1} unlocked after ${contact} was opened or closed when ${lock1} was locked!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,101 +0,0 @@
|
|||||||
/**
|
|
||||||
* Sleep Wizard
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
definition(
|
|
||||||
name: "Smart Sleep",
|
|
||||||
namespace: "smartsolutionsv2/sleeping",
|
|
||||||
author: "SmartThings",
|
|
||||||
description: "Create rules to control devices based on a sleep sensor, including in bed, out of bed, sleeping, and not sleeping.",
|
|
||||||
category: "My Apps",
|
|
||||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/ModeMagic/Cat-ModeMagic.png",
|
|
||||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/ModeMagic/Cat-ModeMagic@2x.png",
|
|
||||||
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/ModeMagic/Cat-ModeMagic@3x.png",
|
|
||||||
singleInstance: true
|
|
||||||
)
|
|
||||||
|
|
||||||
preferences {
|
|
||||||
page(name: "main", title: getLabel("str_Title"), install: true, uninstall: true) {
|
|
||||||
section {
|
|
||||||
app(name: "smartsleepautomation", appName: "Smart Sleep Automation", namespace: "smartsolutionsv2/sleeping", title: getLabel("str_SectionTitle"), multiple: true, uninstall: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def installed() {
|
|
||||||
log.debug "Installed with settings: ${settings}"
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated() {
|
|
||||||
log.debug "Updated with settings: ${settings}"
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
def initialize() {
|
|
||||||
def numChildApps = getChildApps().size();
|
|
||||||
def text = "${numChildApps} rules configured";
|
|
||||||
sendEvent(
|
|
||||||
linkText: numChildApps.toString(),
|
|
||||||
descriptionText: next,
|
|
||||||
eventType: "SOLUTION_SUMMARY",
|
|
||||||
name: "summary",
|
|
||||||
value: numChildApps,
|
|
||||||
data: [["icon": "indicator-dot-gray", "iconColor": "#878787", "value":text]],
|
|
||||||
displayed: false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
cards {
|
|
||||||
card("Action History") {
|
|
||||||
tiles {
|
|
||||||
eventTile { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def getLabel(value)
|
|
||||||
{
|
|
||||||
def str_Title=[
|
|
||||||
"kr": "룰",
|
|
||||||
"us": "Rules"
|
|
||||||
]
|
|
||||||
|
|
||||||
def str_SectionTitle=[
|
|
||||||
"kr": "새로운 자동화 룰 만들기",
|
|
||||||
"us": "New Smart Sleep Automation"
|
|
||||||
]
|
|
||||||
|
|
||||||
if(clientLocale?.language == "ko")
|
|
||||||
{
|
|
||||||
switch(value)
|
|
||||||
{
|
|
||||||
case "str_Title":
|
|
||||||
return str_Title["kr"]
|
|
||||||
case "str_SectionTitle":
|
|
||||||
return str_SectionTitle["kr"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch(value)
|
|
||||||
{
|
|
||||||
case "str_Title":
|
|
||||||
return str_Title["us"]
|
|
||||||
case "str_SectionTitle":
|
|
||||||
return str_SectionTitle["us"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
@@ -258,8 +258,8 @@ def installed() {
|
|||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
log.trace "Updated with settings: ${settings}"
|
log.trace "Updated with settings: ${settings}"
|
||||||
|
unschedule()
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
unschedule()
|
|
||||||
initialize()
|
initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +325,6 @@ def addBulbs() {
|
|||||||
} else {
|
} else {
|
||||||
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
|
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
|
||||||
}
|
}
|
||||||
log.debug "created ${d.displayName} with id $dni"
|
|
||||||
} else {
|
} else {
|
||||||
log.debug "$dni in not longer paired to the Hue Bridge or ID changed"
|
log.debug "$dni in not longer paired to the Hue Bridge or ID changed"
|
||||||
}
|
}
|
||||||
@@ -334,6 +333,8 @@ def addBulbs() {
|
|||||||
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
|
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
|
||||||
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
|
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug "created ${d.displayName} with id $dni"
|
||||||
d.refresh()
|
d.refresh()
|
||||||
} else {
|
} else {
|
||||||
log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'"
|
log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'"
|
||||||
@@ -774,4 +775,4 @@ private Boolean hasAllHubsOver(String desiredFirmware) {
|
|||||||
|
|
||||||
private List getRealHubFirmwareVersions() {
|
private List getRealHubFirmwareVersions() {
|
||||||
return location.hubs*.firmwareVersionString.findAll { it }
|
return location.hubs*.firmwareVersionString.findAll { it }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user