Compare commits

..

1 Commits

21 changed files with 513 additions and 608 deletions

View File

@@ -1,93 +0,0 @@
/**
* TEST
*
* Copyright 2016 박춘영
*
* 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: "TEST", namespace: "스마트보안", author: "박춘영") {
capability "Button"
capability "Samsung TV"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
// TODO: define your main and details tiles here
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'button' attribute
// TODO: handle 'volume' attribute
// TODO: handle 'mute' attribute
// TODO: handle 'pictureMode' attribute
// TODO: handle 'soundMode' attribute
// TODO: handle 'switch' attribute
// TODO: handle 'messageButton' attribute
}
// handle commands
def volumeUp() {
log.debug "Executing 'volumeUp'"
// TODO: handle 'volumeUp' command
}
def volumeDown() {
log.debug "Executing 'volumeDown'"
// TODO: handle 'volumeDown' command
}
def setVolume() {
log.debug "Executing 'setVolume'"
// TODO: handle 'setVolume' command
}
def mute() {
log.debug "Executing 'mute'"
// TODO: handle 'mute' command
}
def unmute() {
log.debug "Executing 'unmute'"
// TODO: handle 'unmute' command
}
def setPictureMode() {
log.debug "Executing 'setPictureMode'"
// TODO: handle 'setPictureMode' command
}
def setSoundMode() {
log.debug "Executing 'setSoundMode'"
// TODO: handle 'setSoundMode' command
}
def on() {
log.debug "Executing 'on'"
// TODO: handle 'on' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def showMessage() {
log.debug "Executing 'showMessage'"
// TODO: handle 'showMessage' command
}

View File

@@ -1,7 +1,7 @@
/**
* Cree Bulb
*
* Copyright 2016 SmartThings
* Copyright 2014 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:
@@ -15,29 +15,29 @@
*/
metadata {
definition (name: "Cree Bulb", namespace: "smartthings", author: "SmartThings") {
definition (name: "Cree Bulb", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Actuator"
capability "Configuration"
capability "Refresh"
capability "Switch"
capability "Switch Level"
capability "Refresh"
capability "Switch"
capability "Switch Level"
fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0000,0019"
}
fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0000,0019"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
// UI tile definitions
// UI tile definitions
tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
@@ -62,12 +62,18 @@ metadata {
def parse(String description) {
log.debug "description is $description"
def resultMap = zigbee.getEvent(description)
def resultMap = zigbee.getKnownDescription(description)
if (resultMap) {
sendEvent(resultMap)
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else {
sendEvent(name: resultMap.type, value: resultMap.value)
}
}
else {
log.debug "DID NOT PARSE MESSAGE for description : $description"
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug zigbee.parseDescriptionAsMap(description)
}
}
@@ -81,7 +87,7 @@ def on() {
}
def setLevel(value) {
zigbee.setLevel(value) + ["delay 500"] + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
zigbee.setLevel(value)
}
def refresh() {

View File

@@ -48,8 +48,8 @@ metadata {
}
standardTile("motion", "device.motion") {
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {

View File

@@ -1,3 +1,4 @@
/**
* Hue Bulb
*
@@ -10,14 +11,13 @@ metadata {
capability "Switch Level"
capability "Actuator"
capability "Color Control"
capability "Color Temperature"
capability "Switch"
capability "Refresh"
capability "Sensor"
command "setAdjustedColor"
command "reset"
command "refresh"
command "reset"
command "refresh"
}
simulator {
@@ -25,7 +25,7 @@ metadata {
}
tiles (scale: 2){
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
@@ -33,58 +33,23 @@ metadata {
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
attributeState "level", action:"switch level.setLevel"
}
tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor"
}
}
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single"
}
standardTile("refresh", "device.switch", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel"
}
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
state "level", label: 'Level ${currentValue}%'
}
controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) {
state "saturation", action:"color control.setSaturation"
}
valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") {
state "saturation", label: 'Sat ${currentValue} '
}
controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) {
state "hue", action:"color control.setHue"
}
valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") {
state "hue", label: 'Hue ${currentValue} '
}
main(["switch"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"])
}
main(["switch"])
details(["switch", "levelSliderControl", "rgbSelector", "refresh", "reset"])
}
// parse events into attributes
@@ -154,27 +119,19 @@ void setColor(value) {
void reset() {
log.debug "Executing 'reset'"
def value = [level:100, hex:"#90C638", saturation:56, hue:23]
setAdjustedColor(value)
def value = [level:100, hex:"#90C638", saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
}
void setAdjustedColor(value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
}
}
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
}
}

View File

@@ -9,59 +9,51 @@ metadata {
definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Color Temperature"
capability "Switch"
capability "Refresh"
capability "Sensor"
command "refresh"
command "refresh"
}
simulator {
// TODO: define status and reply messages here
}
tiles(scale: 2) {
multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
tiles(scale: 2) {
multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
}
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel"
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["switch"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "refresh"])
}
main(["switch"])
details(["rich-control", "refresh"])
}
}
// parse events into attributes
@@ -98,14 +90,6 @@ void setLevel(percent) {
sendEvent(name: "level", value: percent)
}
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
}
}
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()

View File

@@ -14,8 +14,6 @@
*
*/
//DEPRECATED - Using the smartsense-motion-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
metadata {
definition (name: "SmartSense Motion/Temp Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Motion Sensor"
@@ -27,6 +25,10 @@ metadata {
command "enrollResponse"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326"
}
simulator {
@@ -231,7 +233,7 @@ private Map getBatteryResult(rawValue) {
def volts = rawValue / 10
def descriptionText
if (rawValue == 0 || rawValue == 255) {}
if (rawValue == 0) {}
else {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."

View File

@@ -13,7 +13,6 @@
* for the specific language governing permissions and limitations under the License.
*
*/
//DEPRECATED - Using the smartsense-multi-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
metadata {
definition (name: "SmartSense Open/Closed Accelerometer Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -24,7 +23,8 @@
capability "Refresh"
capability "Temperature Measurement"
command "enrollResponse"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321"
}
simulator {
@@ -225,8 +225,7 @@ def getTemperature(value) {
def volts = rawValue / 10
def descriptionText
if (rawValue == 0 || rawValue == 255) {}
else if (volts > 3.5) {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
}
else {

View File

@@ -220,8 +220,7 @@ private Map getBatteryResult(rawValue) {
def volts = rawValue / 10
def descriptionText
if (rawValue == 0 || rawValue == 255) {}
else if (volts > 3.5) {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
}
else {

View File

@@ -196,8 +196,7 @@ private Map getBatteryResult(rawValue) {
def volts = rawValue / 10
def descriptionText
if (rawValue == 0 || rawValue == 255) {}
else if (volts > 3.5) {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
}
else {

View File

@@ -44,7 +44,7 @@ metadata {
attributeState "power", label:'${currentValue} W'
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"

View File

@@ -39,7 +39,7 @@ metadata {
attributeState "level", action:"switch level.setLevel"
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"

View File

@@ -63,7 +63,7 @@ metadata {
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {

View File

@@ -52,7 +52,7 @@
valueTile("battery", "device.battery", inactiveLabel:false, decoration:"flat", width:2, height:2) {
state "battery", label:'${currentValue}% battery', unit:""
}
standardTile("refresh", "device.refresh", inactiveLabel:false, decoration:"flat", width:2, height:2) {
standardTile("refresh", "device.lock", inactiveLabel:false, decoration:"flat", width:2, height:2) {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}

View File

@@ -57,7 +57,7 @@ metadata {
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}

View File

@@ -40,7 +40,7 @@ metadata {
attributeState "power", label:'${currentValue} W'
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"

View File

@@ -42,7 +42,7 @@ metadata {
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"

View File

@@ -54,7 +54,7 @@ metadata {
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}

View File

@@ -0,0 +1,65 @@
/**
* Controlled Power Off
*
* Copyright 2016 Andrew Crow
*
* 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: "Controlled Power Off",
namespace: "acrow311",
author: "Andrew Crow",
description: "Application used to power off devices that should be allowed to finish their cycle before being shut down such as air conditions and tankless water heaters. Application will monitor electric usage and delay shutdown until usage has returned to inactive state.",
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",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
oauth: true)
preferences {
section("Shutdown when device not active") {
input(name: "meter", type: "capability.powerMeter", title: "When This Power Meter...", required: true, multiple: false, description: null)
input(name: "threshold", type: "number", title: "Reports Below...", required: true, description: "In watts, enter integer value")
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
initialize()
}
def initialize() {
unsubscribe()
subscribe(meter, "power", meterHandler)
}
def meterHandler(evt) {
def meterValue = evt.value as double
def thresholdValue = threshold as int
def switchState = meter.currentValue("switch") == "on" // Get current switch status (on = true)
if (switchState) { // If the switch is already off, do nothing
if (meterValue < thresholdValue) { // If the power consumption is low enough, turn off switch
log.info "${meter} reported energy ${meterValue} below ${threshold}. Turning off switch."
sendNotificationEvent("${meter} not running, shutting down.")
meter.off()
} else { // Power consumption too high - device connected to switch in use - do not shutdown.
sendNotificationEvent("Power consumption too high to shut off ${meter}. Attempting again in a moment.")
log.info "${meter} reported energy ${meterValue} above ${threshold}. Leaving switch on."
}
}
}

View File

@@ -545,15 +545,10 @@ def updateSensorData() {
def occupancy = ""
it.capability.each {
if (it.type == "temperature") {
if (it.value == "unknown") {
temperature = "--"
if (location.temperatureScale == "F") {
temperature = Math.round(it.value.toDouble() / 10)
} else {
if (location.temperatureScale == "F") {
temperature = Math.round(it.value.toDouble() / 10)
} else {
temperature = convertFtoC(it.value.toDouble() / 10)
}
temperature = convertFtoC(it.value.toDouble() / 10)
}
} else if (it.type == "occupancy") {

View File

@@ -15,7 +15,7 @@
* for the specific language governing permissions and limitations under the License.
*
*/
definition(
name: "Hue (Connect)",
namespace: "smartthings",
@@ -24,7 +24,7 @@ definition(
category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/hue.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/hue@2x.png",
//singleInstance: true
singleInstance: true
)
preferences {
@@ -58,7 +58,7 @@ def bridgeDiscovery(params=[:])
state.bridges = [:]
state.bridgeRefreshCount = 0
app.updateSetting("selectedHue", "")
}
}
subscribe(location, null, locationHandler, [filterEvents:false])
@@ -130,8 +130,8 @@ def bulbDiscovery() {
def bulboptions = bulbsDiscovered() ?: [:]
def numFound = bulboptions.size() ?: 0
if (numFound == 0)
app.updateSetting("selectedBulbs", "")
app.updateSetting("selectedBulbs", "")
if((bulbRefreshCount % 5) == 0) {
discoverHueBulbs()
}
@@ -140,7 +140,7 @@ def bulbDiscovery() {
section("Please wait while we discover your Hue Bulbs. 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 Bulbs (${numFound} found)", multiple:true, options:bulboptions
}
section {
section {
def title = getBridgeIP() ? "Hue bridge (${getBridgeIP()})" : "Find bridges"
href "bridgeDiscovery", title: title, description: "", state: selectedHue ? "complete" : "incomplete", params: [override: true]
@@ -246,13 +246,13 @@ def installed() {
def updated() {
log.trace "Updated with settings: ${settings}"
unsubscribe()
unschedule()
unsubscribe()
unschedule()
initialize()
}
def initialize() {
log.debug "Initializing"
log.debug "Initializing"
unsubscribe(bridge)
state.inBulbDiscovery = false
state.bridgeRefreshCount = 0
@@ -281,18 +281,18 @@ def uninstalled(){
def bulbListHandler(hub, data = "") {
def msg = "Bulbs list not processed. Only while in settings menu."
def bulbs = [:]
if (state.inBulbDiscovery) {
if (state.inBulbDiscovery) {
def logg = ""
log.trace "Adding bulbs to state..."
state.bridgeProcessedLightList = true
def object = new groovy.json.JsonSlurper().parseText(data)
def object = new groovy.json.JsonSlurper().parseText(data)
object.each { k,v ->
if (v instanceof Map)
if (v instanceof Map)
bulbs[k] = [id: k, name: v.name, type: v.type, hub:hub]
}
}
}
def bridge = null
if (selectedHue)
if (selectedHue)
bridge = getChildDevice(selectedHue)
bridge.sendEvent(name: "bulbList", value: hub, data: bulbs, isStateChange: true, displayed: false)
msg = "${bulbs.size()} bulbs found. ${bulbs}"
@@ -318,7 +318,7 @@ def addBulbs() {
} else {
log.debug "$dni in not longer paired to the Hue Bridge or ID changed"
}
} else {
} else {
//backwards compatable
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
@@ -344,7 +344,7 @@ def addBridge() {
def d = getChildDevice(selectedHue)
if(!d) {
// compatibility with old devices
def newbridge = true
def newbridge = true
childDevices.each {
if (it.getDeviceDataByName("mac")) {
def newDNI = "${it.getDeviceDataByName("mac")}"
@@ -354,10 +354,10 @@ def addBridge() {
it.setDeviceNetworkId("${newDNI}")
if (oldDNI == selectedHue)
app.updateSetting("selectedHue", newDNI)
newbridge = false
newbridge = false
}
}
}
}
}
if (newbridge) {
d = addChildDevice("smartthings", "Hue Bridge", selectedHue, vbridge.value.hub)
log.debug "created ${d.displayName} with id ${d.deviceNetworkId}"
@@ -368,13 +368,13 @@ def addBridge() {
childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port)
childDevice.updateDataValue("networkAddress", vbridge.value.ip + ":" + vbridge.value.port)
} else {
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
}
}
} else {
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
}
}
}
} else {
log.debug "found ${d.displayName} with id $selectedHue already exists"
@@ -436,7 +436,7 @@ def locationHandler(evt) {
dstate.name = "Philips hue ($ip)"
d.sendEvent(name:"networkAddress", value: host)
d.updateDataValue("networkAddress", host)
}
}
}
}
}
@@ -504,11 +504,11 @@ def isValidSource(macAddress) {
/////////////////////////////////////
def parse(childDevice, description) {
def parsedEvent = parseLanMessage(description)
def parsedEvent = parseLanMessage(description)
if (parsedEvent.headers && parsedEvent.body) {
def headerString = parsedEvent.headers.toString()
def bodyString = parsedEvent.body.toString()
if (headerString?.contains("json")) {
if (headerString?.contains("json")) {
def body
try {
body = new groovy.json.JsonSlurper().parseText(bodyString)
@@ -516,11 +516,11 @@ def parse(childDevice, description) {
log.warn "Parsing Body failed - trying again..."
poll()
}
if (body instanceof java.util.HashMap) {
if (body instanceof java.util.HashMap) {
//poll response
def bulbs = getChildDevices()
for (bulb in body) {
def d = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"}
def d = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"}
if (d) {
if (bulb.value.state?.reachable) {
sendEvent(d.deviceNetworkId, [name: "switch", value: bulb.value?.state?.on ? "on" : "off"])
@@ -535,18 +535,18 @@ def parse(childDevice, description) {
}
} else {
sendEvent(d.deviceNetworkId, [name: "switch", value: "off"])
sendEvent(d.deviceNetworkId, [name: "level", value: 100])
sendEvent(d.deviceNetworkId, [name: "level", value: 100])
if (bulb.value.state.sat) {
def hue = 23
def sat = 56
def hex = colorUtil.hslToHex(23, 56)
sendEvent(d.deviceNetworkId, [name: "color", value: hex])
sendEvent(d.deviceNetworkId, [name: "hue", value: hue])
sendEvent(d.deviceNetworkId, [name: "saturation", value: sat])
}
sendEvent(d.deviceNetworkId, [name: "saturation", value: sat])
}
}
}
}
}
}
else
{ //put response
@@ -595,7 +595,7 @@ def parse(childDevice, description) {
}
}
}
}
} else {
log.debug "parse - got something other than headers,body..."
return []
@@ -616,7 +616,7 @@ def off(childDevice) {
def setLevel(childDevice, percent) {
log.debug "Executing 'setLevel'"
def level
def level
if (percent == 1) level = 1 else level = Math.min(Math.round(percent * 255 / 100), 255)
put("lights/${getId(childDevice)}/state", [bri: level, on: percent > 0])
}
@@ -633,14 +633,6 @@ def setHue(childDevice, percent) {
put("lights/${getId(childDevice)}/state", [hue: level])
}
def setColorTemperature(childDevice, huesettings) {
log.debug "Executing 'setColorTemperature($huesettings)'"
def ct = Math.round(Math.abs((huesettings / 12.96829971181556) - 654))
def value = [ct: ct, on: true]
log.trace "sending command $value"
put("lights/${getId(childDevice)}/state", value)
}
def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'"
def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
@@ -697,7 +689,7 @@ HOST: ${host}
}
private put(path, body) {
def host = getBridgeIP()
def host = getBridgeIP()
def uri = "/api/${state.username}/$path"
def bodyJSON = new groovy.json.JsonBuilder(body).toString()
def length = bodyJSON.getBytes().size().toString()
@@ -723,11 +715,11 @@ private getBridgeIP() {
host = d.getDeviceDataByName("networkAddress")
else
host = d.latestState('networkAddress').stringValue
}
}
if (host == null || host == "") {
def serialNumber = selectedHue
def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value
if (!bridge) {
if (!bridge) {
bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(serialNumber) }?.value
}
if (bridge?.ip && bridge?.port) {
@@ -737,9 +729,9 @@ private getBridgeIP() {
host = "${convertHexToIP(bridge?.ip)}:${convertHexToInt(bridge?.port)}"
} else if (bridge?.networkAddress && bridge?.deviceAddress)
host = "${convertHexToIP(bridge?.networkAddress)}:${convertHexToInt(bridge?.deviceAddress)}"
}
}
log.trace "Bridge: $selectedHue - Host: $host"
}
}
return host
}

View File

@@ -1,317 +1,317 @@
/**
* 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.
*
* Smart Security
*
* Author: SmartThings
* Date: 2013-03-07
*/
definition(
name: "Smart Security",
namespace: "smartthings",
author: "SmartThings",
description: "Alerts you when there are intruders but not when you just got up for a glass of water in the middle of the night",
category: "Safety & Security",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/SafetyAndSecurity/App-IsItSafe.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/SafetyAndSecurity/App-IsItSafe@2x.png"
)
preferences {
section("Sensors detecting an intruder") {
input "intrusionMotions", "capability.motionSensor", title: "Motion Sensors", multiple: true, required: false
input "intrusionContacts", "capability.contactSensor", title: "Contact Sensors", multiple: true, required: false
}
section("Sensors detecting residents") {
input "residentMotions", "capability.motionSensor", title: "Motion Sensors", multiple: true, required: false
}
section("Alarm settings and actions") {
input "alarms", "capability.alarm", title: "Which Alarm(s)", multiple: true, required: false
input "silent", "enum", options: ["Yes","No"], title: "Silent alarm only (Yes/No)"
input "seconds", "number", title: "Delay in seconds before siren sounds"
input "lights", "capability.switch", title: "Flash these lights (optional)", multiple: true, required: false
input "newMode", "mode", title: "Change to this mode (optional)", required: false
}
section("Notify others (optional)") {
input "textMessage", "text", title: "Send this message", multiple: false, required: false
input("recipients", "contact", title: "Send notifications to") {
input "phone", "phone", title: "To this phone", multiple: false, required: false
}
}
section("Arm system when residents quiet for (default 3 minutes)") {
input "residentsQuietThreshold", "number", title: "Time in minutes", required: false
}
}
def installed() {
log.debug "INSTALLED"
subscribeToEvents()
state.alarmActive = null
}
def updated() {
log.debug "UPDATED"
unsubscribe()
subscribeToEvents()
unschedule()
state.alarmActive = null
state.residentsAreUp = null
state.lastIntruderMotion = null
alarms?.off()
}
private subscribeToEvents()
{
subscribe intrusionMotions, "motion", intruderMotion
subscribe residentMotions, "motion", residentMotion
subscribe intrusionContacts, "contact", contact
subscribe alarms, "alarm", alarm
subscribe(app, appTouch)
}
private residentsHaveBeenQuiet()
{
def threshold = ((residentsQuietThreshold != null && residentsQuietThreshold != "") ? residentsQuietThreshold : 3) * 60 * 1000
def result = true
def t0 = new Date(now() - threshold)
for (sensor in residentMotions) {
def recentStates = sensor.statesSince("motion", t0)
if (recentStates.find{it.value == "active"}) {
result = false
break
}
}
log.debug "residentsHaveBeenQuiet: $result"
result
}
private intruderMotionInactive()
{
def result = true
for (sensor in intrusionMotions) {
if (sensor.currentMotion == "active") {
result = false
break
}
}
result
}
private isResidentMotionSensor(evt)
{
residentMotions?.find{it.id == evt.deviceId} != null
}
def appTouch(evt)
{
alarms?.off()
state.alarmActive = false
}
// Here to handle old subscriptions
def motion(evt)
{
if (isResidentMotionSensor(evt)) {
log.debug "resident motion, $evt.name: $evt.value"
residentMotion(evt)
}
else {
log.debug "intruder motion, $evt.name: $evt.value"
intruderMotion(evt)
}
}
def intruderMotion(evt)
{
if (evt.value == "active") {
log.debug "motion by potential intruder, residentsAreUp: $state.residentsAreUp"
if (!state.residentsAreUp) {
log.trace "checking if residents have been quiet"
if (residentsHaveBeenQuiet()) {
log.trace "calling startAlarmSequence"
startAlarmSequence()
}
else {
log.trace "calling disarmIntrusionDetection"
disarmIntrusionDetection()
}
}
}
state.lastIntruderMotion = now()
}
def residentMotion(evt)
{
// Don't think we need this any more
//if (evt.value == "inactive") {
// if (state.residentsAreUp) {
// startReArmSequence()
// }
//}
}
def contact(evt)
{
if (evt.value == "open") {
// TODO - check for residents being up?
if (!state.residentsAreUp) {
if (residentsHaveBeenQuiet()) {
startAlarmSequence()
}
else {
disarmIntrusionDetection()
}
}
}
}
def alarm(evt)
{
log.debug "$evt.name: $evt.value"
if (evt.value == "off") {
alarms?.off()
state.alarmActive = false
}
}
private disarmIntrusionDetection()
{
log.debug "residents are up, disarming intrusion detection"
state.residentsAreUp = true
scheduleReArmCheck()
}
private scheduleReArmCheck()
{
def cron = "0 * * * * ?"
schedule(cron, "checkForReArm")
log.debug "Starting re-arm check, cron: $cron"
}
def checkForReArm()
{
def threshold = ((residentsQuietThreshold != null && residentsQuietThreshold != "") ? residentsQuietThreshold : 3) * 60 * 1000
log.debug "checkForReArm: threshold is $threshold"
// check last intruder motion
def lastIntruderMotion = state.lastIntruderMotion
log.debug "checkForReArm: lastIntruderMotion=$lastIntruderMotion"
if (lastIntruderMotion != null)
{
log.debug "checkForReArm, time since last intruder motion: ${now() - lastIntruderMotion}"
if (now() - lastIntruderMotion > threshold) {
log.debug "re-arming intrusion detection"
state.residentsAreUp = false
unschedule()
}
}
else {
log.warn "checkForReArm: lastIntruderMotion was null, unable to check for re-arming intrusion detection"
}
}
private startAlarmSequence()
{
if (state.alarmActive) {
log.debug "alarm already active"
}
else {
state.alarmActive = true
log.debug "starting alarm sequence"
sendPush("Potential intruder detected!")
if (newMode) {
setLocationMode(newMode)
}
if (silentAlarm()) {
log.debug "Silent alarm only"
alarms?.strobe()
if (location.contactBookEnabled) {
sendNotificationToContacts(textMessage ?: "Potential intruder detected", recipients)
}
else {
if (phone) {
sendSms(phone, textMessage ?: "Potential intruder detected")
}
}
}
else {
def delayTime = seconds
if (delayTime) {
alarms?.strobe()
runIn(delayTime, "soundSiren")
log.debug "Sounding siren in $delayTime seconds"
}
else {
soundSiren()
}
}
if (lights) {
flashLights(Math.min((seconds/2) as Integer, 10))
}
}
}
def soundSiren()
{
if (state.alarmActive) {
log.debug "Sounding siren"
if (location.contactBookEnabled) {
sendNotificationToContacts(textMessage ?: "Potential intruder detected", recipients)
}
else {
if (phone) {
sendSms(phone, textMessage ?: "Potential intruder detected")
}
}
alarms?.both()
if (lights) {
log.debug "continue flashing lights"
continueFlashing()
}
}
else {
log.debug "alarm activation aborted"
}
unschedule("soundSiren") // Temporary work-around to scheduling bug
}
def continueFlashing()
{
unschedule()
if (state.alarmActive) {
flashLights(10)
schedule(util.cronExpression(now() + 10000), "continueFlashing")
}
}
private flashLights(numFlashes) {
def onFor = 1000
def offFor = 1000
log.debug "FLASHING $numFlashes times"
def delay = 1L
numFlashes.times {
log.trace "Switch on after $delay msec"
lights?.on(delay: delay)
delay += onFor
log.trace "Switch off after $delay msec"
lights?.off(delay: delay)
delay += offFor
}
}
private silentAlarm()
{
silent?.toLowerCase() in ["yes","true","y"]
}
/**
* 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.
*
* Smart Security
*
* Author: SmartThings
* Date: 2013-03-07
*/
definition(
name: "Smart Security",
namespace: "smartthings",
author: "SmartThings",
description: "Alerts you when there are intruders but not when you just got up for a glass of water in the middle of the night",
category: "Safety & Security",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/SafetyAndSecurity/App-IsItSafe.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/SafetyAndSecurity/App-IsItSafe@2x.png"
)
preferences {
section("Sensors detecting an intruder") {
input "intrusionMotions", "capability.motionSensor", title: "Motion Sensors", multiple: true, required: false
input "intrusionContacts", "capability.contactSensor", title: "Contact Sensors", multiple: true, required: false
}
section("Sensors detecting residents") {
input "residentMotions", "capability.motionSensor", title: "Motion Sensors", multiple: true, required: false
}
section("Alarm settings and actions") {
input "alarms", "capability.alarm", title: "Which Alarm(s)", multiple: true, required: false
input "silent", "enum", options: ["Yes","No"], title: "Silent alarm only (Yes/No)"
input "seconds", "number", title: "Delay in seconds before siren sounds"
input "lights", "capability.switch", title: "Flash these lights (optional)", multiple: true, required: false
input "newMode", "mode", title: "Change to this mode (optional)", required: false
}
section("Notify others (optional)") {
input "textMessage", "text", title: "Send this message", multiple: false, required: false
input("recipients", "contact", title: "Send notifications to") {
input "phone", "phone", title: "To this phone", multiple: false, required: false
}
}
section("Arm system when residents quiet for (default 3 minutes)") {
input "residentsQuietThreshold", "number", title: "Time in minutes", required: false
}
}
def installed() {
log.debug "INSTALLED"
subscribeToEvents()
state.alarmActive = null
}
def updated() {
log.debug "UPDATED"
unsubscribe()
subscribeToEvents()
unschedule()
state.alarmActive = null
state.residentsAreUp = null
state.lastIntruderMotion = null
alarms?.off()
}
private subscribeToEvents()
{
subscribe intrusionMotions, "motion", intruderMotion
subscribe residentMotions, "motion", residentMotion
subscribe intrusionContacts, "contact", contact
subscribe alarms, "alarm", alarm
subscribe(app, appTouch)
}
private residentsHaveBeenQuiet()
{
def threshold = ((residentsQuietThreshold != null && residentsQuietThreshold != "") ? residentsQuietThreshold : 3) * 60 * 1000
def result = true
def t0 = new Date(now() - threshold)
for (sensor in residentMotions) {
def recentStates = sensor.statesSince("motion", t0)
if (recentStates.find{it.value == "active"}) {
result = false
break
}
}
log.debug "residentsHaveBeenQuiet: $result"
result
}
private intruderMotionInactive()
{
def result = true
for (sensor in intrusionMotions) {
if (sensor.currentMotion == "active") {
result = false
break
}
}
result
}
private isResidentMotionSensor(evt)
{
residentMotions?.find{it.id == evt.deviceId} != null
}
def appTouch(evt)
{
alarms?.off()
state.alarmActive = false
}
// Here to handle old subscriptions
def motion(evt)
{
if (isResidentMotionSensor(evt)) {
log.debug "resident motion, $evt.name: $evt.value"
residentMotion(evt)
}
else {
log.debug "intruder motion, $evt.name: $evt.value"
intruderMotion(evt)
}
}
def intruderMotion(evt)
{
if (evt.value == "active") {
log.debug "motion by potential intruder, residentsAreUp: $state.residentsAreUp"
if (!state.residentsAreUp) {
log.trace "checking if residents have been quiet"
if (residentsHaveBeenQuiet()) {
log.trace "calling startAlarmSequence"
startAlarmSequence()
}
else {
log.trace "calling disarmIntrusionDetection"
disarmIntrusionDetection()
}
}
}
state.lastIntruderMotion = now()
}
def residentMotion(evt)
{
// Don't think we need this any more
//if (evt.value == "inactive") {
// if (state.residentsAreUp) {
// startReArmSequence()
// }
//}
}
def contact(evt)
{
if (evt.value == "open") {
// TODO - check for residents being up?
if (!state.residentsAreUp) {
if (residentsHaveBeenQuiet()) {
startAlarmSequence()
}
else {
disarmIntrusionDetection()
}
}
}
}
def alarm(evt)
{
log.debug "$evt.name: $evt.value"
if (evt.value == "off") {
alarms?.off()
state.alarmActive = false
}
}
private disarmIntrusionDetection()
{
log.debug "residents are up, disarming intrusion detection"
state.residentsAreUp = true
scheduleReArmCheck()
}
private scheduleReArmCheck()
{
def cron = "0 * * * * ?"
schedule(cron, "checkForReArm")
log.debug "Starting re-arm check, cron: $cron"
}
def checkForReArm()
{
def threshold = ((residentsQuietThreshold != null && residentsQuietThreshold != "") ? residentsQuietThreshold : 3) * 60 * 1000
log.debug "checkForReArm: threshold is $threshold"
// check last intruder motion
def lastIntruderMotion = state.lastIntruderMotion
log.debug "checkForReArm: lastIntruderMotion=$lastIntruderMotion"
if (lastIntruderMotion != null)
{
log.debug "checkForReArm, time since last intruder motion: ${now() - lastIntruderMotion}"
if (now() - lastIntruderMotion > threshold) {
log.debug "re-arming intrusion detection"
state.residentsAreUp = false
unschedule()
}
}
else {
log.warn "checkForReArm: lastIntruderMotion was null, unable to check for re-arming intrusion detection"
}
}
private startAlarmSequence()
{
if (state.alarmActive) {
log.debug "alarm already active"
}
else {
state.alarmActive = true
log.debug "starting alarm sequence"
sendPush("Potential intruder detected!")
if (newMode) {
setLocationMode(newMode)
}
if (silentAlarm()) {
log.debug "Silent alarm only"
alarms?.strobe()
if (location.contactBookEnabled) {
sendNotificationToContacts(textMessage ?: "Potential intruder detected", recipients)
}
else {
if (phone) {
sendSms(phone, textMessage ?: "Potential intruder detected")
}
}
}
else {
def delayTime = seconds
if (delayTime) {
alarms?.strobe()
runIn(delayTime, "soundSiren")
log.debug "Sounding siren in $delayTime seconds"
}
else {
soundSiren()
}
}
if (lights) {
flashLights(Math.min((seconds/2) as Integer, 10))
}
}
}
def soundSiren()
{
if (state.alarmActive) {
log.debug "Sounding siren"
if (location.contactBookEnabled) {
sendNotificationToContacts(textMessage ?: "Potential intruder detected", recipients)
}
else {
if (phone) {
sendSms(phone, textMessage ?: "Potential intruder detected")
}
}
alarms?.both()
if (lights) {
log.debug "continue flashing lights"
continueFlashing()
}
}
else {
log.debug "alarm activation aborted"
}
unschedule("soundSiren") // Temporary work-around to scheduling bug
}
def continueFlashing()
{
unschedule()
if (state.alarmActive) {
flashLights(10)
schedule(util.cronExpression(now() + 10000), "continueFlashing")
}
}
private flashLights(numFlashes) {
def onFor = 1000
def offFor = 1000
log.debug "FLASHING $numFlashes times"
def delay = 1L
numFlashes.times {
log.trace "Switch on after $delay msec"
lights?.on(delay: delay)
delay += onFor
log.trace "Switch off after $delay msec"
lights?.off(delay: delay)
delay += offFor
}
}
private silentAlarm()
{
silent?.toLowerCase() in ["yes","true","y"]
}