Best SmartApp for Complex "follow me" Dimming Setups?

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()
}
2 Likes

Looks great! Especially like the ratio dimming section.