I think I’ve got this worked out for you. I used the Big Switch app as a model. That app allows a switch to follow on and off commands, only on commands, only off, etc… In this new version, I similarly separated out the dimmer slaves from the on/off, only on and only off slaves. It means for a typical dim with me, you need to include the slaves twice; but there’s a lot more flexibility now.
For your scenario, you’ll need to include the dining table lights in two sections: 1) The “on, but not off” (Slave On). 2) The appropriate dimming offset section. I think this will work nicely. Not a big fan of the UI, but it was a lot easier to code than providing groups and then letting the user select which commands to follow.
/**
* Dim With Me - plus offset
*
* Original Author: todd@wackford.net
* Date: 2013-11-12
*/
/**
* App Name: Dim With Me plus Offset
*
* Author: Todd Wackford
* twack@wackware.net
* Date: 2013-11-12
* Version: 0.2
*
* Use this program with a virtual dimmer as the master for best results.
*
* This app lets the user select from a list of dimmers to act as a triggering
* master for other dimmers or regular switches. Regular switches come on
* anytime the master dimmer is on or dimmer level is set to more than 0%.
* of the master dimmer.
*
******************************************************************************
* Changes
******************************************************************************
*
* Change 1: 2014-10-22 (wackford)
* Fixed bug in setlevelwhen on/off was coming in
*
* Change 2: 2014-11-01 (wackford)
* added subscription to switch.level event. Shouldn't change much
* but some devices only sending level event and not setLevel.
*
* Change 3: 2015-05-05 (sticks18)
* added ability to offset slave dimmers in case different lights
* dim differently or have varying lumen output.
*
******************************************************************************
Other Info: Special thanks to Danny Kleinman at ST for helping me get the
state stuff figured out. The Android state filtering had me
stumped.
*
******************************************************************************
*/
// Automatically generated. Make future change here.
definition(
name: "Dim With Me plus Offset",
namespace: "wackware",
author: "todd@wackford.net",
description: "Follows the dimmer level of another dimmer",
category: "My Apps",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png"
)
preferences {
page(name: "selectMaster", title: "Choose a Master, then choose Slaves to follow...", install: true, uninstall: true) {
section("When this...") {
input "masters", "capability.switchLevel",
multiple: false,
title: "Master Dimmer Switch...",
required: true
}
section("Then these will follow on and off commands...") {
input "slaves2", "capability.switch",
multiple: true,
title: "Slave On/Off Switch(es)...",
required: false
}
section("Then these will follow on commands, but not off...") {
input "slaves3", "capability.switch",
multiple: true,
title: "Slave On Switch(es)...",
required: false
}
section("Then these will follow off commands, but not on...") {
input "slaves4", "capability.switch",
multiple: true,
title: "Slave Off Switch(es)...",
required: false
}
section("And these will follow with the same dimming level, but not on or off...") {
input "slaves", "capability.switchLevel",
multiple: true,
title: "Slave Dimmer Switch(es)...",
required: false
}
section("Slaves with dimming offset (will not follow on/off commands, please include above if desired)...") {
href "addOffset", title:"Additive Offset (Slave = Master Level + Offset)"
href "minusOffset", title: "Negative Offset (Slave = Master Level - Offset)"
href "ratioOffset", title: "Ratio Offset (Slave X% of Master Level)"
}
section("Name and Mode...") {
label title: "Assign a name", required: false
mode title: "Set for specific mode(s)", required: false
}
}
page(name: "addOffset", title: "addOffset")
page(name: "minusOffset", title: "minusOffset")
page(name: "ratioOffset", title: "ratioOffset")
}
def addOffset() {
dynamicPage(name: "addOffset", title: "Select Additive Offset Slave Dimmers...", install:false, uninstall: false) {
section("Additive Offset Group 1") {
input "slavesAdd1", "capability.switchLevel", title: "Slave Dimmers...", multiple: true, required: false
input "levelAdd1", "number", title: "Level Offset (number to be added to master level)", required: false
}
section("Additive Offset Group 2") {
input "slavesAdd2", "capability.switchLevel", title: "Slave Dimmers...", multiple: true, required: false
input "levelAdd2", "number", title: "Level Offset (number to be added to master level)", required: false
}
}
}
def minusOffset() {
dynamicPage(name: "minusOffset", title: "Select Negative Offset Slave Dimmers...", install:false, uninstall: false) {
section("Negative Offset Group 1") {
input "slavesMin1", "capability.switchLevel", title: "Slave Dimmers...", multiple: true, required: false
input "levelMin1", "number", title: "Level Offset (number to be subtracted from master level)", required: false
}
section("Negative Offset Group 2") {
input "slavesMin2", "capability.switchLevel", title: "Slave Dimmers...", multiple: true, required: false
input "levelMin2", "number", title: "Level Offset (number to be subtracted from master level)", required: false
}
}
}
def ratioOffset() {
dynamicPage(name: "ratioOffset", title: "Select Ratio Offset Slave Dimmers...", install:false, uninstall: false) {
section("Ratio Offset Group 1") {
input "slavesRatio1", "capability.switchLevel", title: "Slave Dimmers...", multiple: true, required: false
input "levelRatio1", "number", title: "X% of Master Level (ie: 10 = 0.10 * Master Level, 110 = 1.10 * Master Level)", required: false
}
section("Ratio Offset Group 2") {
input "slavesRatio2", "capability.switchLevel", title: "Slave Dimmers...", multiple: true, required: false
input "levelRatio2", "number", title: "X% of Master Level (ie: 10 = 0.10 * Master Level, 110 = 1.10 * Master Level)", required: false
}
}
}
def installed()
{
subscribe(masters, "switch.on", switchOnHandler)
subscribe(masters, "switch.off", switchOffHandler)
subscribe(masters, "switch.setLevel", switchSetLevelHandler)
subscribe(masters, "switch", switchSetLevelHandler)
subscribe(masters, "level", switchSetLevelHandler)
}
def updated()
{
unsubscribe()
subscribe(masters, "switch.on", switchOnHandler)
subscribe(masters, "switch.off", switchOffHandler)
subscribe(masters, "switch.setLevel", switchSetLevelHandler)
subscribe(masters, "switch", switchSetLevelHandler)
subscribe(masters, "level", switchSetLevelHandler)
log.info "subscribed to all of switches events"
}
def switchSetLevelHandler(evt)
{
if ((evt.value == "on") || (evt.value == "off" ))
return
def level = evt.value.toFloat()
level = level.toInteger()
log.info "switchSetLevelHandler Event: ${level}"
if (levelAdd1) {
def lvlAdd1 = level + levelAdd1
lvlAdd1 = lvlAdd1.toInteger()
if (lvlAdd1 > 100) {lvlAdd1 = 100}
if ((lvlAdd1 < 1) && (level > 0)) {lvlAdd1 = 1}
log.info "switchSetLevelHandler Add 1 Offset Event: ${lvlAdd1}"
slavesAdd1?.setLevel(lvlAdd1)
}
if (levelAdd2) {
def lvlAdd2 = level + levelAdd2
lvlAdd2 = lvlAdd2.toInteger()
if (lvlAdd2 > 100) {lvlAdd2 = 100}
if ((lvlAdd2 < 1) && (level > 0)) {lvlAdd2 = 1}
log.info "switchSetLevelHandler Add 2 Offset Event: ${lvlAdd2}"
slavesAdd2?.setLevel(lvlAdd2)
}
if (levelMin1) {
def lvlMin1 = level - levelMin1
lvlMin1 = lvlMin1.toInteger()
if (lvlMin1 > 100) {lvlMin1 = 100}
if ((lvlMin1 < 1) && (level > 0)) {lvlMin1 = 1}
log.info "switchSetLevelHandler Minus 1 Offset Event: ${lvlMin1}"
slavesMin1?.setLevel(lvlMin1)
}
if (levelMin2) {
def lvlMin2 = level - levelMin2
lvlMin2 = lvlMin2.toInteger()
if (lvlMin2 > 100) {lvlMin2 = 100}
if ((lvlMin2 < 1) && (level > 0)) {lvlMin2 = 1}
log.info "switchSetLevelHandler Minus 2 Offset Event: ${lvlMin2}"
slavesMin2?.setLevel(lvlMin2)
}
if (levelRatio1) {
def lvlRatio1 = levelRatio1.toFloat()
lvlRatio1 = lvlRatio1 / 100
lvlRatio1 = lvlRatio1 * level
lvlRatio1 = lvlRatio1.toInteger()
if (lvlRatio1 > 100) {lvlRatio1 = 100}
if ((lvlRatio1 < 1) && (level > 0)) {lvlRatio1 = 1}
log.info "switchSetLevelHandler Ratio 1 Offset Event: ${lvlRatio1}"
slavesRatio1?.setLevel(lvlRatio1)
}
if (levelRatio2) {
def lvlRatio2 = levelRatio2.toFloat()
lvlRatio2 = lvlRatio2 / 100
lvlRatio2 = lvlRatio2 * level
lvlRatio2 = lvlRatio2.toInteger()
if (lvlRatio2 > 100) {lvlRatio2 = 100}
if ((lvlRatio2 < 1) && (level > 0)) {lvlRatio2 = 1}
log.info "switchSetLevelHandler Ratio 2 Offset Event: ${lvlRatio2}"
slavesRatio2?.setLevel(lvlRatio2)
}
slaves?.setLevel(level)
}
def switchOffHandler(evt) {
log.info "switchoffHandler Event: ${evt.value}"
slaves4?.off()
slaves2?.off()
}
def switchOnHandler(evt) {
log.info "switchOnHandler Event: ${evt.value}"
def dimmerValue = masters.latestValue("level") //can be turned on by setting the level
slaves3?.on()
slaves2?.on()
}