Groups Request - Add Fans

Hi,

Would it be possible to add Fans under the Groups and Scenes app so Ceiling Fans are properly supported? I would love to be able to use RM to set the fan speed for a group or turn fans off.

Tagging @bravenel, wasn't sure who to tag exactly.

1 Like

I haven't tried this myself but I believe fans should show up under "dimmers" even in group options. This way you should be able to control them using their "level" for speeds.

I put together a quick and dirty app to do just that. Fans are very different than lights in HE now so adding them to groups really isn't the way to go.

Parent App:

/**
 *  ****************  Fan Sync Parent App  ****************
 *
 *  Design Usage:
 *  Keep Fans in sync - ON/OFF 
 *
 *  Code and ideas used from Jason Bottjen (JasonJoel on Hubitat forum)
 *  
 *-------------------------------------------------------------------------------------------------------------------
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 * ------------------------------------------------------------------------------------------------------------------------------
 *
 *  Changes:
 *
 *  V1.0.0 - 12/31/18 - Initial release.
 *
 *//**
 *  ****************  Fan Sync Parent App  ****************
 *
 *  Design Usage:
 *  Keep Fans in sync - ON/OFF 
 *
 *  Code and ideas used from Jason Bottjen (JasonJoel on Hubitat forum)
 *  
 *-------------------------------------------------------------------------------------------------------------------
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 * ------------------------------------------------------------------------------------------------------------------------------
 *
 *  Changes:
 *
 *  V1.0.0 - 12/31/18 - Initial release.
 *
 */

definition(
    name:"Fan Sync",
    namespace: "ryancasler",
    author: "Ryan Casler",
    description: "Keep Fans in sync - ON/OFF",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "",
    iconX3Url: "",
)

preferences {
     page name: "mainPage", title: "", install: true, uninstall: true
} 

def installed() {
    log.debug "Installed with settings: ${settings}"
    initialize()
}

def updated() {
    log.debug "Updated with settings: ${settings}"
    unsubscribe()
    initialize()
}

def initialize() {
    log.info "There are ${childApps.size()} child apps"
    childApps.each {child ->
    log.info "Child app: ${child.label}"
    }
}

def mainPage() {
    dynamicPage(name: "mainPage") {
    	installCheck()
		if(state.appInstalled == 'COMPLETE'){
			section(getFormat("title", "${app.label}")) {
				paragraph "<div style='color:#00CED1'>Keep Fans in sync - ON/OFF</div>"
				paragraph getFormat("line")
			}
			section("Instructions:", hideable: true, hidden: true) {
				paragraph "<b>Notes:</b>"
				paragraph "- Add master and slave fanes to keep in sync.<br>"
			}
  			section(getFormat("header-darkcyan", " Child Apps")) {
				app(name: "anyOpenApp", appName: "Fan Sync Child", namespace: "ryancasler", title: "<b>Add a new 'Fan Sync' child</b>", multiple: true)
  			}
 			section(getFormat("header-darkcyan", " General")) {
       				label title: "Enter a name for parent app (optional)", required: false
 			}
			display()
		}
	}
}

def installCheck(){         
	state.appInstalled = app.getInstallationState() 
	if(state.appInstalled != 'COMPLETE'){
		section{paragraph "Please hit 'Done' to install '${app.label}' parent app "}
  	}
  	else{
    	log.info "Parent Installed OK"
  	}
}

def getFormat(type, myText=""){
	if(type == "header-green") return "<div style='color:#ffffff;font-weight: bold;background-color:#81BC00;border: 1px solid;box-shadow: 2px 3px #A9A9A9'>${myText}</div>"
	if(type == "header-darkcyan") return "<div style='color:#ffffff;font-weight: bold;background-color:#008B8B;border: 1px solid;box-shadow: 2px 3px #A9A9A9'>${myText}</div>"
	if(type == "line") return "\n<hr style='background-color:#00CED1; height: 1px; border: 0;'></hr>"
	if(type == "title") return "<h2 style='color:#00CED1;font-weight: bold;font-style: italic'>${myText}</h2>"
}

def display(){
	section() {
		paragraph getFormat("line")
		paragraph "<div style='color:#00CED1;text-align:center'>Fan Sync - App Version: 1.0.0</div>"
	}       
}          

Child App:

/**
 *  ****************  Fan Sync Child App  ****************
 *

 *  Design Usage:
 *  Keep Fans in sync - ON/OFF 
 *
 *  Code and ideas used from Jason Bottjen (JasonJoel on Hubitat forum)
 *  
 *-------------------------------------------------------------------------------------------------------------------
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 * ------------------------------------------------------------------------------------------------------------------------------
 *
 *  Changes:
 *
 *  V1.0.0 - 12/31/18 - Initial release.
 *
 */

definition(
    name:"Fan Sync Child",
    namespace: "ryancasler",
    author: "Ryan Casler",
    description: "Keep Fans in sync - ON/OFF",
    category: "",

	parent: "ryancasler:Fan Sync",
    
    iconUrl: "",
    iconX2Url: "",
    iconX3Url: "",
)

preferences {
    page(name: "pageConfig")
}

def installed() {
    log.debug "Installed with settings: ${settings}"
    initialize()
}

def updated() {
    log.debug "Updated with settings: ${settings}"
    unsubscribe()
    initialize()
}

def initialize() {
	setDefaults()
	if(pause1==false){subscribeNow()}
}

def pageConfig() {
    dynamicPage(name: "pageConfig", title: "<h2 style='color:#00CED1;font-weight: bold'>Fan Sync</h2>", nextPage: null, install: true, uninstall: true, refreshInterval:0) {	
	display()
    
	section("Instructions:", hideable: true, hidden: true) {
		paragraph "<b>Notes:</b>"
		paragraph "- Select master and slave fans you want to keep in sync<br>- The slave(s) will follow the master."
	}
		
	section(getFormat("header-darkcyan", " Select Master Fan Device")) {
		input "masterFan", "capability.fanSpeed", title: "Select Master Fan Device", submitOnChange: true, hideWhenEmpty: true, required: true, multiple: false
	}
	section(getFormat("header-darkcyan", " Select Slave Fan Device(s)")) {
		input "slaveFan", "capability.fanSpeed", title: "Select Slave Fan Device(s)", submitOnChange: true, hideWhenEmpty: true, required: true, multiple: true
	}
	section(getFormat("header-darkcyan", " General")) {label title: "Enter a name for this child app", required: false}
	section() {
		input(name: "logEnable", type: "bool", defaultValue: "true", title: "Enable Debug Logging", description: "Enable extra logging for debugging.")
   	}
	display2()
	}
}

def display() {
	section() {
		paragraph getFormat("line")
		input "pause1", "bool", title: "Pause This App", required: true, submitOnChange: true, defaultValue: false
	}
}

def display2() {
	section() {
		paragraph getFormat("line")
		paragraph "<div style='color:#00CED1;text-align:center'>Fan Sync - App Version: 1.0.0</div>"
	}
}

def getFormat(type, myText=""){
	if(type == "header-green") return "<div style='color:#ffffff;font-weight: bold;background-color:#81BC00;border: 1px solid;box-shadow: 2px 3px #A9A9A9'>${myText}</div>"
	if(type == "header-darkcyan") return "<div style='color:#ffffff;font-weight: bold;background-color:#008B8B;border: 1px solid;box-shadow: 2px 3px #A9A9A9'>${myText}</div>"
    if(type == "line") return "\n<hr style='background-color:#00CED1; height: 1px; border: 0;'></hr>"
	if(type == "title") return "<div style='color:#00CED1;font-weight: bold; font-style: italic'>${myText}</div>"
}

def LOGDEBUG(txt){
    try {
		if (settings.logEnable) { log.debug("${app.label} - ${txt}") }
    } catch(ex) {
    	log.error("${app.label} - LOGDEBUG unable to output requested data!")
    }
}

def pauseOrNot(){
	LOGDEBUG("In pauseOrNot...")
    state.pauseNow = pause1
        if(state.pauseNow == true){
            state.pauseApp = true
            if(app.label){
            if(app.label.contains('red')){
                log.warn "Paused"}
            else{app.updateLabel(app.label + ("<font color = 'red'> (Paused) </font>" ))
              LOGDEBUG("App Paused - state.pauseApp = $state.pauseApp ")   
            }
            }
        }
     if(state.pauseNow == false){
         state.pauseApp = false
         if(app.label){
     if(app.label.contains('red')){ app.updateLabel(app.label.minus("<font color = 'red'> (Paused) </font>" ))
     	LOGDEBUG("App Released - state.pauseApp = $state.pauseApp ")                          
        }
     }
  }    
}

def setDefaults(){
    pauseOrNot()
    if(pause1 == null){pause1 = false}
    if(state.pauseApp == null){state.pauseApp = false}
	if(logEnable == null){logEnable = false}
}

def subscribeNow() {
	unsubscribe()
	subscribe(masterFan, "fanSpeed", masterSpeedHandler) 
}

def masterSpeedHandler(evt){
	LOGDEBUG("Event Value: " + evt.value)
	if(evt.value == "high"){
		LOGDEBUG("High Check True")
		slaveFan.each{
			it.setSpeed(high)
		}
	}
	if(evt.value == "off"){
		LOGDEBUG("OFF Check True")
		slaveFan.each{
			it.setSpeed(off)
		}
	}
	if(evt.value == "low"){
		LOGDEBUG("Low Speed")
		slaveFan.each{
			it.setSpeed(low)
		}
	}
	if(evt.value == "medium"){
		LOGDEBUG("Medium Speed")
		slaveFan.each{
			it.setSpeed(medium)
		}
	}
}

Works for me...you're welcome to edit/modify/change whatever you want to make it work for you.

I would recommend creating a "virtual fan" device first and using that as the master device. There's also a function to control simple switches (for floor fans controlled via smartplugs).

Good point, I just realized it doesn't appear to be showing as a Dimmer for me when using HubConnect since it doesn't have a level option, just the speed options. I manually added that to this driver to fix that issue so now it shows in Groups app.

Thanks for the suggestion, I'll give it a try. I think having an option in Groups for fans would be a good addition but that app looks like it'll work out for what I wanted in this instance.

1 Like

I agree adding fans to the groups option......is definitely the way to go!

@Ryan780

Just moved from Wink to Hubitat this past weekend. I'm also extremely new at coding, so I apologize if this is a fundamental question.

I copied your code (did not do any modifications) and installed it on my hub. I started it up and clicked on "set master," and nothing happens. I'm assuming that it should list out my devices and appears to be having an issue finding them. I'm assuming this because as soon as I click on "set master", and spinning blue cycle appears in the top right corner of the page.

Is there some level of configuration I need to do to adapt your program?

This only works for devices with the Fan Speed capability. Do you have any fans in your setup? I would recommend using a virtual fan as your master.

I have eight(8) Hampton Bay Zigbee Fan Controllers connected to the device. I did select to have the device create a fan and light in addition to the controller. Maybe that is the issue. I did create a virtual device for the master and assigned the drive as Hampton Bay Zigbee Fan Controller. But it doesn't find that either.

Wait, what? I am using it to control 2 Hampton Bay and one GE fan controllers. I told you, create a Virtual Fan device using the Virtual Fan driver. Select that as your master. Or don't use the app. Dunno what else to tell you.

Again I apologize if I'm missing the obvious. I have created a virtual Device using the Virtual Fan Driver as you suggested. I click on you app and select the only option "Add a new 'Fan Sync' Child". I click on "Click to set" under "Select Master Fan Device". Nothing comes up. I physically type the Virtual Device name into the field an it appears to take it. I then proceed to click on "Click to set" under "Select Slave Fan Device" and agin, nothing appears to select. So I tried to type the name of one of the fans and it will not accept it. So I'm not sure what I'm doing wrong. When I click on "Click to set", what should happen?

@Ryan780 I delete the app and reinstalled it. It's now allowing them to select devices. I must have not copied the code correctly. So the problem was on my end. Thanks for your help,

3 Likes

Happy to have found this great app, I would have thought using groups would work, as the fans show up as dimmers, but apparently not.

@Ryan780 I now am having the same issue as tom5 did; whereas, the "Click to Set" function brings up no devices. I have deleted the app and code 3 times and it still sits and waits for me to type the device name in, whereas I believe a list is supposed to pop up. Perhaps this is being caused by me adding the app remotely via Chrome remote desktop?? I have re-copied and replaced the code several times to no avail. Any, suggestions are appreciated!

Nope. You have to have a device that has the Fan capability as your master device.

I do, it is prompting me to type the device names on the line, is this correct or is there supposed to be a list of devices with radio boxes to check? "Click to set" does nothing..

I have a Virtual Fan controller that I wanted to use as the master..

These are the fans I wanted to combine:

Copy the app again. You shouldn't have to type anything.

I ended up copying your fan code from another post you did and the select device function now works..weird.... Anyhow, for some reason only one of the three fans responds to the virtual switch used to activate this app, I had experienced the same issue when I tried to create a rule tom combine these fans. The logs only show the command going to one fan as well. Any ideas?

What driver are you using for the 3 fans? What type of devices are they?

They are standard ceiling fans. The switches are the latest zwave plus GE smart fan control switches using the GE Smart Fan Control driver. The devices are in the screenshots in this thread.