/* Change Log:
v1.1 Make Label show status of aggregated motion. Will require loading and saving you name again
v1.0 Initial
*/
definition (
name: "Motion Aggregation Child",
namespace: "DTTerastar",
author: "Darrell Turner",
description: "Aggregation for Motion / Contact devices",
category: "Convenience",
parent: "DTTerastar:Motion Aggregation",
iconUrl: "",
iconX2Url: ""
)
preferences {
section() {
page name: "mainPage", title: "", install: true, uninstall: true
}
}
def mainPage() {
dynamicPage(name: "mainPage") {
section(title: "") {
input "myName", "string", title: "Name:", required: false, defaultValue: null, submitOnChange: true
}
section("Primary Motion") {
input "primaryDevs", "capability.motionSensor", title: "Primary motion detectors (these can start aggregated motion and continue it)", multiple: true
input "primaryIncr", "number", title: "If motion detected, turn the aggregated motion on for an addl X minutes", required: true, defaultValue: 5
input "primaryDecr", "bool", title: "Only start counting down once all primary are inactive", defaultValue: true
input "primaryMax", "number", title: "Maximum countdown minutes due to primary motion sensors", defaultValue: 30
}
section("Secondary Motion") {
input "secondaryDevs", "capability.motionSensor", title: "Secondary motion detectors (these can continue aggregated motion)", multiple: true
input "secondaryIncr", "number", title: "If motion detected, keep the aggregated motion on for X minutes", required: true, defaultValue: 5
input "secondaryDecr", "bool", title: "Only start counting down once all secondary are inactive", defaultValue: true
input "secondaryMax", "number", title: "Maximum countdown minutes due to secondary motion sensors", defaultValue: 30
}
section("Contacts") {
input "contactDevs", "capability.contactSensor", title: "Contact sensors", multiple: true
input "contactOpenIncr", "number", title: "If a contact opens, turn the aggregated motion on for X minutes"
input "contactCloseIncr", "number", title: "If a contact closes, turn the aggregated motion on for X minutes"
input "contactDecr", "enum", title: "Countdown when", defaultValue: "always", options:[["always": "always"], ["closed": "all contacts are closed"], ["open": "all contacts are open"]]
input "contactMax", "number", title: "Maximum countdown minutes due to contact sensors", defaultValue: 30
}
section("Switches") {
input "switchDevs", "capability.switch", title: "Switches", multiple: true
input "switchOnIncr", "number", title: "If a switch turns on, turn the aggregated motion on for X minutes"
input "switchOffIncr", "number", title: "If a switch turns off, turn the aggregated motion on for X minutes"
input "switchDecr", "enum", title: "Countdown when", defaultValue: "always", options:[["always": "always"], ["off": "all switches are off"], ["on": "all switches are on"]]
input "switchMax", "number", title: "Maximum countdown minutes due to switch changes", defaultValue: 30
}
section("") {
input "debugOutput", "bool", title: "Enable debug logging?", defaultValue: true, displayDuringSetup: false, required: false
}
}
}
def childDevices() {
return getChildDevices()?.find { true }
}
def installed() {
logDebug "Installed with settings: ${settings}"
initialize()
}
def updated() {
logDebug "Updated with settings: ${settings}"
unsubscribe()
initialize()
if (settings?.debugOutput) runIn(1800,logsOff)
}
def initialize() {
logDebug "initialize()"
createChildDevices()
if(primaryDevs) subscribe(primaryDevs, "motion", primaryHandler)
if(secondaryDevs) subscribe(secondaryDevs, "motion", secondaryHandler)
if(contactDevs) subscribe(contactDevs, "contact", contactHandler)
if(switchDevs) subscribe(switchDevs, "switch", switchHandler)
schedule("0 * * * * ? *", elapsed)
}
def elapsed() {
if (state.primaryMins>0) {
if (!primaryDecr || primaryDevs?.every { it.currentValue("motion") == "inactive" })
state.primaryMins-=1
}
if (state.secondaryMins>0) {
if (!secondaryDecr || secondaryDevs?.every { it.currentValue("motion") == "inactive" })
state.secondaryMins-=1
}
if (state.contactMins>0) {
if ((contactDecr!="open" && contactDecr!="closed") || contactDevs?.every { it.currentValue("contact") == contactDecr })
state.contactMins-=1
}
if (state.switchMins>0) {
if ((switchDecr!="on" && switchDecr!="off") || switchDevs?.every { it.currentValue("switch") == switchDecr })
state.switchMins-=1
}
refresh()
}
def status() {
def s = []
if (state.primaryMins?:0>0) s << "Primary ${state.primaryMins?:0}m"
if (state.secondaryMins?:0>0) s << "Secondary ${state.secondaryMins?:0}m"
if (state.contactMins?:0>0) s << "Contact ${state.contactMins?:0}m"
if (state.switchMins?:0>0) s << "Switch ${state.switchMins?:0}m"
return s.join(" ");
}
def refresh() {
if (primaryMax>0 && state.primaryMins>primaryMax) state.primaryMins=primaryMax
if (secondaryMax>0 && state.secondaryMins>secondaryMax) state.secondaryMins=secondaryMax
if (contactMax>0 && state.contactMins>contactMax) state.contactMins=contactMax
if (switchMax>0 && state.switchMins>switchMax) state.switchMins=switchMax
if (isActive) {
if (state.primaryMins>0 || state.secondaryMins>0 || state.contactMins>0 || state.switchMins>0) {
status = status()
logDebug "${myName} not turning off: ${status}"
app.updateLabel("${myName} ${status}")
return;
}
app.updateLabel("${myName} Inactive")
childDevices.each { it.inactive() }
} else {
if (state.primaryMins>0 || state.contactMins>0 || state.switchMins>0) {
status = status()
logDebug "${myName} turning ON: ${status}"
app.updateLabel("${myName} ${status}")
childDevices.each { it.active() }
}
}
}
private void createChildDevices() {
app.updateLabel(myName)
if (childDevices) {
childDevices.each {
it.name = myName + " Aggregate Motion"
}
} else {
addChildDevice("hubitat", "Virtual Motion Sensor", UUID.randomUUID().toString(), null, [completedSetup: true, isComponent: true, name: myName + " Aggregate Motion"])
}
}
def primaryHandler(evt) {
if(evt.value == "inactive" && primaryIncr>0) {
state.primaryMins = (state.primaryMins?:0)+primaryIncr
}
refresh()
}
def secondaryHandler(evt) {
if(evt.value == "inactive" && secondaryIncr>0) {
state.secondaryMins = (state.secondaryMins?:0)+secondaryIncr
}
refresh()
}
def contactHandler(evt) {
if(evt.value == "open" && contactOpenIncr>0) {
state.contactMins = (state.contactMins?:0)+contactOpenIncr
}
if(evt.value == "closed" && contactCloseIncr>0) {
state.contactMins = (state.contactMins?:0)+contactCloseIncr
}
refresh()
}
def switchHandler(evt) {
if(evt.value == "on" && switchOnIncr>0) {
state.switchMins = (state.switchMins?:0)+switchOnIncr
}
if(evt.value == "off" && contactOffIncr>0) {
state.switchMins = (state.switchMins?:0)+switchOffIncr
}
refresh()
}
def getIsActive() {
return (childDevices?.any { sensor -> sensor.currentValue("motion") == "active" });
}
def logsOff(){
log.warn "debug logging disabled..."
app.updateSetting("debugOutput",[value:"false",type:"bool"])
}
private logDebug(msg) {
if (settings?.debugOutput) {
log.debug msg
}
}