Okay, I've decided to give ChatGPT a go as well.
I love Room Lighting, but there is an issue that has bugged me, maybe I just don't know how to use Room Lighting properly for this feature, where the "light levels" are not updating correctly for modes that have the "activation" option unticked for certain modes, all the lights are activated at a previous mode level.
Anyway, I've played around with ChatGPT and after some trial and error, I've got the code below.
- activation based on motion sensors or manual switch
- lights off after specified motion delay for both cases
- Modes can be selected for manual activation, so not activated by motion
- And, I believe it keeps all light levels as specified for all modes.
Will test for the next few days and see how well it works for my use case!
definition(
name: "Mode-Based Lighting",
namespace: "iEnam-ChatGPT",
author: "iEnam-ChatGPT",
description: "Lighting control with manual activation override for specific modes.",
category: "Convenience",
iconUrl: "",
iconX2Url: ""
)
preferences {
page(name: "mainPage", title: "Configure Mode-Based Lighting", install: true, uninstall: true) {
section("App Name") {
input "appName", "text", title: "Enter a custom app name", required: true
}
section("Select Lights") {
input "lights", "capability.switchLevel", title: "Select lights to control", multiple: true, required: true
}
section("Motion Sensors (optional)") {
input "motionSensors", "capability.motionSensor", title: "Select motion sensors to activate lights", multiple: true, required: false
}
section("Lighting Levels by Mode") {
location.modes.each { mode ->
input "level_${mode}", "number", title: "Light level for ${mode} mode (0-100)", required: false, range: "0..100"
}
}
section("Manual Activation Modes") {
input "manualModes", "mode", title: "Select modes for manual activation", multiple: true, required: false
}
section("Options") {
input "turnOffDelay", "number", title: "Turn off delay (in seconds) after motion stops", required: false, defaultValue: 300
}
}
}
def installed() {
initialize()
}
def updated() {
unsubscribe()
unschedule()
initialize()
}
def initialize() {
state.lightActive = false // Initialize light active status
updateAppLabel() // Update app label with the initial status
if (motionSensors) {
subscribe(motionSensors, "motion.active", motionActiveHandler)
subscribe(motionSensors, "motion.inactive", motionInactiveHandler)
}
subscribe(lights, "switch.on", manualLightHandler)
subscribe(lights, "switch.on", lightOnHandler) // Monitor lights turned on externally
subscribe(location, "mode", modeChangeHandler)
}
def modeChangeHandler(evt) {
log.debug "Mode changed to ${evt.value}"
adjustLightsForMode()
}
def motionActiveHandler(evt) {
def currentMode = location.mode
log.debug "Motion detected on ${evt.device.displayName} in mode: ${currentMode}"
if (manualModes?.contains(currentMode)) {
log.debug "Mode '${currentMode}' is set for manual activation. Skipping motion-triggered light activation."
return
}
activateLightsForMode()
}
def motionInactiveHandler(evt) {
log.debug "Motion stopped on ${evt.device.displayName}"
// Check if all motion sensors are inactive
def allInactive = motionSensors?.every { sensor -> sensor.currentMotion == "inactive" }
if (allInactive) {
log.debug "All motion sensors are inactive. Starting turn-off delay."
if (turnOffDelay) {
runIn(turnOffDelay, turnOffLights) // Delay in seconds
} else {
turnOffLights()
}
} else {
log.debug "Some motion sensors are still active. Turn-off delay not started."
}
}
def manualLightHandler(evt) {
def currentMode = location.mode
log.debug "Light manually turned on: ${evt.device.displayName}"
def lightLevel = settings["level_${currentMode}"]
if (lightLevel) {
log.debug "Adjusting manual light to level ${lightLevel} for mode ${currentMode}"
evt.device.setLevel(lightLevel.toInteger())
state.lightActive = true
updateAppLabel() // Update the app label when light status changes
}
}
def lightOnHandler(evt) {
// Handle when a light is turned on manually or externally
def currentMode = location.mode
log.debug "Light turned on externally: ${evt.device.displayName}"
def lightLevel = settings["level_${currentMode}"]
if (lightLevel) {
log.debug "Adjusting light to level ${lightLevel} for mode ${currentMode}"
evt.device.setLevel(lightLevel.toInteger())
}
// When a light is turned on externally, check motion sensors' state
checkMotionAndDelayTurnOff(evt.device)
}
def checkMotionAndDelayTurnOff(light) {
// Check if all motion sensors are inactive
def allInactive = motionSensors?.every { sensor -> sensor.currentMotion == "inactive" }
if (allInactive) {
log.debug "No motion detected. Scheduling light off after delay."
if (turnOffDelay) {
runIn(turnOffDelay, turnOffLights) // Delay in seconds
} else {
turnOffLights()
}
} else {
log.debug "Motion detected, not turning off light."
}
}
def adjustLightsForMode() {
def currentMode = location.mode
def lightLevel = settings["level_${currentMode}"]
if (lightLevel) {
lights.each { light ->
if (light.currentValue("switch") == "on") { // Adjust only if the light is already on
log.debug "Adjusting ${light.displayName} to level ${lightLevel} for mode ${currentMode}"
light.setLevel(lightLevel.toInteger())
} else {
log.debug "${light.displayName} is off. Skipping adjustment."
}
}
}
}
def activateLightsForMode() {
def currentMode = location.mode
def lightLevel = settings["level_${currentMode}"]
if (lightLevel) {
lights.each { light ->
log.debug "Activating ${light.displayName} to level ${lightLevel} for mode ${currentMode}"
light.setLevel(lightLevel.toInteger())
light.on()
}
state.lightActive = true
}
updateAppLabel() // Update the app label when light status changes
}
def turnOffLights() {
log.debug "Turning off lights"
lights.each { light ->
light.off()
}
state.lightActive = false
updateAppLabel() // Update the app label when light status changes
log.debug "Light status set to: INACTIVE"
}
def updateAppLabel() {
def status = state.lightActive ? "ACTIVE" : "INACTIVE"
def statusColor = state.lightActive ? "#008000" : "#808080" // Green for active, grey for inactive
def displayName = settings.appName ?: "Mode-Based Lighting" // Use custom name or fallback to default
app.updateLabel("<font color='${statusColor}'>${displayName} [${status}]</font>")
}
// --- BUTTON ACTIONS ---
def activeLights() {
log.debug "Manually setting lights to active mode"
activateLightsForMode()
}
def turnOff() {
log.debug "Manually turning off lights"
turnOffLights()
}
def updateSettings() {
log.debug "Manually updating settings and adjusting lights"
adjustLightsForMode()
}
// Button Methods
def mainPage() {
dynamicPage(name: "mainPage", title: "Control Mode-Based Lighting", install: true, uninstall: true) {
section("Controls") {
paragraph "Use the buttons below to control lights manually:"
input name: "activeButton", type: "button", title: "Active"
input name: "turnOffButton", type: "button", title: "Turn Off"
input name: "updateButton", type: "button", title: "Update"
}
}
}