Binding to a switch that has level and switch properties through MQTT

I am attempting to bind some items that I have attached to a SmartThings hub through MQTT. The devices are actually Keen Smart Vents, but they behave like dimmer switches. They have a “switch” value containing on/off state and a “level” value which has the level from 1-100.

I would like to bind these to openHAB items, preferably to a single Dimmer item. I thought I might be able to do this purely in the Item definition since an item allows multiple MQTT bindings, but I haven’t been able to get a solution that works perfectly.

The main challenge I seem to be running into at this point is that when sending commands back to SmartThings through MQTT, if the switch is in the OFF position (or 0 in my openHAB dimmer), the ON command first needs to be sent to SmartThings, and then the Level command after it’s on.

Is it possible to do this in just the item configuration? Or should I be attempting to manage this in rules? Can I do it with just one openHAB Item, or do I need to have two for each vent to parallel the settings in SmartThings?

Personally I would not try to do this all in one Item even if it were possible (which I don’t think it is given you need to send two commands sometimes). Instead:

  • create a separate item for each vent as a Dimmer
  • create a Group:Dimmer:MIN and make all of your vents be a member of this Group. Whenever you need to send the same command to all of the vents send it to this Group. The MIN will make the Group’s state the minimum of the states of all the members.
  • You will have to use a rule to first send the ON command and then the new level.

The easiest way to do this is to create a proxy Dimmer Item that isn’t bound to anything. You put this proxy on your sitemap. Create a rule that triggers when this proxy Item changes.

In the rule, check whether the state of the Group Item is 0. If any one of the Group’s members are OFF the Group’s state will be OFF as well (i.e. 0). If it is 0 then send an ON command to the Group, sleep for a second or so, then send the new Dimmer number. Any command sent to the Group will be forwarded to all the members of said Group.

NOTE: while you can send ON/OFF to a Dimmer, it stores its state as a Percent so when a Dimmer is OFF its state will be 0.

1 Like

Hi Rich,

Thanks for the response. I see from your answer to my question that I was a bit imprecise in what I was asking. I don’t want to control all of my smart vents with a single openHAB item. Instead, I want to control each of my SmartThings vents (both level and switch properties) with its own single openHAB item.

Your answer helps me along I think. I am going to try creating a smartVentProxy item which will show in my sitemap. I will also have smartVentLevel and smartVentSwitch items which will have mqtt rules on their definition. The smartVentProxy item will have a rule attached to it which updates the corresponding switch and item as needed, and vice versa for the smartVentLevel and smartVentSwitch items.

Is that along the lines of what you were thinking for a single vent?

Yes. You may need to make the item actually bound to the MQTT topic a String. If you make it a Dimmer I think it will convert the ON to 100 before sending the command to the topic.

I got this working. Thanks for the help.

I didn’t need to use a string because a normal switch (as opposed to a dimmer) uses ON and OFF rather than numbers.

Here are my configs, in case any one is interested.

Number officeSmartVentProxy "Office Smart Vent [%d %%]"           <heating> (Temperature)                                                                           	
Dimmer officeSmartVentLevel  "Office Smart Vent Level [%d %%]"    <heating> (Temperature) {mqtt="<[mosquitto:smartthings/Office Smart Vent/level:state:default],
                                                                                      >[mosquitto:smartthings/Office Smart Vent/level:command:*:default]"}
Switch officeSmartVentSwitch  "Office Smart Vent Switch [%s]"  (Temperature) {mqtt="<[mosquitto:smartthings/Office Smart Vent/switch:state:MAP(onOffLowerToUpper.map)],
 		                                                                            >[mosquitto:smartthings/Office Smart Vent/switch:command:*:MAP(onOffUpperToLower.map)"}                                                         		

And here are my rules, using the jsr223 engine and javascript. I required that the items follow a naming pattern so that I can calculate the Proxy, Switch, and Level item name based on the name of the item that triggered the rule.


var mqttSmartVentProxyRule = new Rule(){
    getEventTrigger: function () {
        return [
        new CommandEventTrigger("officeSmartVentProxy", null)
            ]
    },
    execute: function (event) {
    	var proxy = event.getItem();
    	var itemName = proxy.getName();
    	var command = event.getCommand();
    	
       	if(command == null){ return; } 
       
        oh.logInfo("mqttSmartVentProxy", "Smart Vent Proxy recived command, item=[{}], command=[{}], event=[{}]", itemName, command, event);
 
    	var switchName = itemName.substring(0, itemName.length() - 5) + "Switch";
    	var switchItem = ir.getItem(switchName);
    	
    	var levelName = itemName.substring(0, itemName.length() - 5) + "Level";
    	var levelItem = ir.getItem(levelName);
    	
    	if(command == 0){
    		oh.logInfo("mqttSmartVentProxy", "State is 0");
    		oh.logInfo("mqttSmartVentProxy", "switchItem=[{}]", switchItem);
    		be.sendCommand(switchItem, OnOffType.OFF);
    		be.postUpdate(levelItem, 0);
    	}
    	else{
    		oh.logInfo("mqttSmartVentProxy", "Nonzero state is [{}]", command);
    		
    		if(switchItem.state == OnOffType.OFF){
    			oh.logInfo("mqttSmartVentProxy", "Turning switch OFF");
    			be.sendCommand(switchItem, OnOffType.ON);
    		}
    		
    		be.sendCommand(levelItem, command);
    	}
    }
}
 

var mqttSmartVentLevelRule = new Rule(){
    getEventTrigger: function () {
        return [
            new ChangedEventTrigger("officeSmartVentLevel")
            ]
    },
    execute: function (event) {
    	var levelItem = event.getItem();
    	var itemName = levelItem.getName();
        
        oh.logInfo("mqttSmartVentLevel", "Smart Vent Level received command, item=[{}], event=[{}]", itemName, event);
 
 		var proxyName = itemName.substring(0, itemName.length() - 5) + "Proxy";
    	var proxyItem = ir.getItem(proxyName);
    	
 		if(levelItem.state != proxyItem.state){
 			oh.logInfo("mqttSmartVentLevel", "Got a different value. Updating proxy.");
 			be.postUpdate(proxyItem, event.getNewState());
 		}
    }
}


var mqttSmartVentSwitchRule = new Rule(){
    getEventTrigger: function () {
        return [
            new ChangedEventTrigger("officeSmartVentSwitch")
            ]
    },
    execute: function (event) {
    	var switchItem = event.getItem();
    	var itemName = switchItem.getName();
        
        oh.logInfo("mqttSmartVentSwitch", "Smart Vent Switch received command, item=[{}], event=[{}]", itemName, event);
 		
 		var proxyName = itemName.substring(0, itemName.length() - 6) + "Proxy";
    	var proxyItem = ir.getItem(proxyName);
    	
    	var levelName = itemName.substring(0, itemName.length() - 6) + "Level";
    	var levelItem = ir.getItem(levelName);
    	
    	var prevLevel = pe.previousState(levelItem, true);
    	var prevState = null;
    	if(prevLevel != null){
    		prevState = prevLevel.state.toString();
    	}
    	
    	oh.logInfo("mqttSmartVentSwitch", "Current Level state = [{}], previous=[{}]", levelItem.state, prevState);
 
 		setTimeout(function(){
	 		if(event.getNewState() == OnOffType.OFF) {
	 			be.postUpdate(levelItem, 0);
	 		} 
	 		else if(prevState != null && levelItem.state == 0){
	 		    be.postUpdate(levelItem, prevState);
	 		}
 		}, 5);
 		
    }
}

I might have a few small issues to work out as occasionally there is some weird behavior when a vent is switched from off to on or vice versa, but overall it is working quite well.