Virtual fan controller with only low, medium, high?

Reviving a question from earlier than still seems open. The current version of the "Virtual Fan Controller" automatically implements the following fan functions:

Low
Medium-Low
Medium
Medium-High
High
On
Off
Auto

Is there any way to limit these to just Off, Low, Med, High? I don’t think this can be done within the current virtual driver but if not is there any other driver out there that would accomplish the same thing?

My reason here is that I’ve set up a number of ceiling fans on a SharpTools dashboard working through a Broadlink IR/RF blaster. It’s confusing for users when they tap the fan button and are presented with the long list of options there. Yes I know I can use rules such that Medium-low just maps to medium, etc. but I would love to find a way so that the pop up only shows Off, Low, Med, and High instead of the longer list.

I’ve tried the "Enhanced Virtual Fan Controller” but I’ve had no luck getting that to restrict the pop up options.

Thanks in advance for any thoughts!

Not sure whether it would work for you, but I recently tasked Chat GPT with creating a driver for me which I called ‘Virtual Switch - Multi’. I meant to start a thread and post the code but had a couple of features I wanted to add (and never got around to yet)

The driver allows you to create a definable number of virtual switch child devices (up to 5). The important feature that may work for you, is that it has two modes - multi and exclusive. In multi it’s just bunch of switches that are independent of one another. However in exclusive mode it works as follows:

  • turning any child on turns on the parent
  • Turning any other child on, turns off the previously activated child (so you could use low, medium, high in a 3 child switch and switching from one to another cancels previous)
  • Any child on turns on the parent
  • All off turns off the parent
  • Child devices can be separately labelled from the parent

I needed this ‘exclusive’ mode for an automation at some point previously and couldn’t find anything built in to do it. I resorted to using a virtual thermostat and setting temperatures to 0, 1, 2 and 3.

I’ll post the code in another thread and link it here later in case it’s of interest (though I’d think that a built in virtual driver with this functionality is missing and could be useful to others)

Easy fix may be to update the supportedFanSpeeds attribute to only have the speeds desired via a small utlity app - looks like the enum may be derived from the attribute. When I get to my desk I'll try it and if it works I'll post one for you.

1 Like

I did something similar with buttons on the dashboard to activate different picture modes on my TVs.

But here, I’m using the built in “fan” device that’s supported in SharpTools. It’s a nice single button that saves space and when you tap it, the pop up comes up and displays all of the speed options (along with on, off, and auto). I’d like to be able to limit that pop up to display only off, low, med, and high, and keep the whole interface to that single button on the dashboard.

1 Like

I wrote a virtual fan driver that simulates the functions of the GE Fan controller. It only uses high-med-low and it also has the level capability.

https://raw.githubusercontent.com/cburges2/Hubitat/refs/heads/main/Drivers%20Code/Virtual%20Fan%20Driver.groovy

What I hate is that Legacy Dashboards Fan Template will not respect the supported fan speeds and adjust the selection menu to reflect that. Unlike the Thermostat tile, which changes the menu based on what is in the supported attributes.

So bad news, while I can change the attribute as desired it doesn't drive the ENUM values in the Set Speed command; also turns out that you can't dynamic alter an ENUM (I keep forgetting that). So best I can do is make it easier for you to customize your driver. Below is the source for a device driver that mimics the built in Virtual Fan Controller. On line 2 you should see a line that looks like:

@Field fanSpeeds = ["low","medium-low","medium","medium-high","high","on","off","auto"]

If you alter that line to only include that speeds you want and save it should give you what you want.

Driver Source
import groovy.transform.Field
@Field fanSpeeds = ["low","medium-low","medium","medium-high","high","on","off","auto"]

metadata {
    definition (name: "Custom Virtual Fan Controller", 
                namespace: "thebearmay", 
                author: "Jean P. May, Jr") {
        
        capability "FanControl"
        
        attribute "speed", "ENUM"
        attribute "supportedFanSpeeds","JSON_OBJECT"
        
        command 'setSpeed', [[defaultValue:'high', name:'Fan speed*', description:'Fan speed to set', type:'ENUM', constraints:fanSpeeds]]
        command 'cycleSpeed'
        
        

    }

    preferences {
        input "logEnable", "bool", title: "Enable debug logging", defaultValue: true
    }
}

def installed() {
    log.info "Custom Virtual Fan Controller Installed"
    initialize()
}

def updated() {
    log.info "Custom Virtual Fan Controller Updated"
    initialize()
}

def initialize() {
    if (device.currentValue("speed") == null){
        sendEvent(name: "speed", value: "off")
        state.lastSpeed = 'off'
    }
    if (device.currentValue("supportedFanSpeeds") == null) {
    	sendEvent(name:"supportedFanSpeeds", value: groovy.json.JsonOutput.toJson(fanSpeeds), descriptionText:"supportedFanSpeeds intialized")
    }

}

def cycleSpeed(){
    suptSpeeds = device.currentValue("supportedFanSpeeds")
	inx = suptSpeeds.indexOf(state.lastSpeed)
    inx++
    if(inx > suptSpeeds.size())
        inx = 0
    sendEvent(name: "speed", value: suptSpeeds[inx], descriptionText:"Fan speed changed from ${state.lastSpeed} to ${suptSpeeds[inx]}" )
    state.lastSpeed = suptSpeeds[inx]
}
                
def setSpeed(String speed) {
    if (logEnable) log.debug "setSpeed(${speed})"
    if(!device.currentValue("supportedFanSpeeds").find("$speed"))
    	return
    
    if(speed == 'auto') {
		sendEvent(name: "speed", value: "low", descriptionText:"Fan speed changed from ${state.lastSpeed} to Auto->Low")
    } else {
		sendEvent(name: "speed", value: speed, descriptionText:"Fan speed changed from ${state.lastSpeed} to ${speed}")
    }
    state.lastSpeed = speed
}

Thank you!! This is perfect and does exactly what I needed! Much appreciated!

Thank you so much! I will test this out along with the custom driver that chrisbvt provided as well. Much appreciated!

1 Like