A Sonoff iFan03, flashed with Tasmota and connected to OH via MQTT
Light Channel MB-Light linked to Item: MasterBedroom_Light which is a switch type
Fan Channel MB-Fan linked to Item: MasterBedroom_Fan which is Dimmer type. Channel is set to be a number of either 0, 1, 2 or 3.
A Zooz On/Off Z-Wave switch.
Switch Channel linked to Item: MasterBedroom_Light_Zooz which is switch type
A Zooz Dimmer Z-Wave dimmer switch
Dimmer channel linked to Item: MasterBedroom_Fan_Zooz which is a dimmer type
I have a rule that ties MasterBedroom_Light_Zooz and MasterBedroom_Light together nicely, and works perfectly.
What I need to work out how to do next is write a rule to that ties the MasterBedroom_Fan_Zooz to the MasterBedroom_Fan so that the dimmer can set the fan to one of the 4 levels mentioned above. But I really have no idea where to start. I know I have to somehow translate the dimmers 0 - 100% to the 0, 1, 2 or 3 that the iFan expects, but am not sure the best way to do that? Or would I somehow leverage scenes to do this (the Zooz Dimmer does support these)?
I have been reading the documentation and reviewing a lot of code examples, and I have a feel for the flow and syntax of the code, but still am not too sure where/how to start this.
So does anyone have a rule that more or less does this or something similar that I can dissect and change? I tend to learn to code by doing, rather than reading other code. And that actually sparks another question, is there a way to debug rule code? An output console or something like it?
rule "master bedroom ceiling fan control"
when
Item MasterBedroom_Fan changed or
Item MasterBedroom_Fan_Zooz changed
then
if (triggeringItem.state != MasterBedroom_Fan.state) {
MasterBedroom_Fan.sendCommand(triggeringItem.state.toString)
} else if (triggeringItem.state != MasterBedroom_Fan_Zooz.state) {
MasterBedroom_Fan_Zooz.sendCommand(triggeringItem.state.toString)
}
end
This ties the two items together, and does work. I am just not sure how to convert the percentage from MasterBedroom_Fan_Zooz to 0, 1, 2 or 3 and pass that to MasterBedroom_Fan and vice versa.
Case statements work in Rules DSL. Hereās what I use for my rule-based thermostat, and it should translate pretty easily into what you want to do. In this case, Iām actually using IF statements within the case to determine if a value is between a threshold.
switch (Temperature_Google_Mode.state) {
case "heat" :
{
if (CurrentTemp < TargetTemp && Maker_Fireplace.state == OFF) { Maker_Fireplace.sendCommand(ON) }
if (CurrentTemp > TargetTemp && Maker_Fireplace.state == ON) { Maker_Fireplace.sendCommand(OFF) }
}
case "cool" :
{
if (CurrentTemp > TargetTemp && Outlet_Air_Conditioner.state == OFF) { Outlet_Air_Conditioner.sendCommand(ON) }
if (CurrentTemp < TargetTemp && Outlet_Air_Conditioner.state == ON) { Outlet_Air_Conditioner.sendCommand(OFF) }
}
case "off" :
{
Temperature_Google_Mode.sendCommand("off")
}
}
I donāt know how to make a case fit in a threshold (1-33), but you could just use IF statements and I doubt youād see any difference in performance.
I wouldnāt say āineloquentā. Itās just straightforward and simple. But Iām not sure what your concern is with the fade-in of the dimmer, so maybe Iām missing something.
rule "master bedroom ceiling fan control"
when
Item MasterBedroom_Fan changed or
Item MasterBedroom_Fan_Zooz changed
then
if MasterBedroom_fan changed <---- This is probably not right.
{
switch (MasterBedroom_Fan.state) {
case 0 :
{
Masterbedroom_Fan_Zooz.sendCommand(0)
}
case 1 :
{
Masterbedroom_Fan_Zooz.sendCommand(25)
}
case 2 :
{
Masterbedroom_Fan_Zooz.sendCommand(50)
}
case 3 :
{
Masterbedroom_Fan_Zooz.sendCommand(75)
}
}
}
end
That is my start here. But I an not sure about that first if statement. How do I separate out which item has changed and case from that?
Or can I do something like this:
when
Item MasterBedroom changed
then
<<<<< switch block >>>>
when
Item MasterBedroom_Fan_Zooz changed
then
<<<< switch block >>>>
Actually, there might be a better way to think of this. When you increment MasterBedroom_Fan_Zooz by tapping the switch, it probably has set values that it goes to, rather than just jumping up by a percentage. If so, you can identify those values and use those in the case statement, and thereās no need to use thresholds.
It occurs to me that you should also use ReentranceLock to guard against concurrency. Otherwise, the rule will fire multiple times while youāre adjusting the dimmer. Thereās a section on it in the Rules documentation.
This dimmer is a hold down and let go when ready type dimmer, so there are not set increments on button push. So having a case statement with a wide margin of error is probably a good thing. Based on how I am seeing this thing work with OH, I am guessing the rule wonāt fire till I release the button and the switch reports back to OH
Okay, thatās good (but Iād definitely check by observing the behaviour in your log).
Youāre correct that the first IF statement doesnāt work. āitem changedā only works for triggers, not in rule logic. Thinking about this more, though, you really donāt need the case statement. Just use a series of IF statements so that you can isolate each possible scenario. For the thresholds, it would be:
if (MasterBedroom_Fan_Zooz.state > 0 && MasterBedroom_Fan_Zooz.state <= 33) { Masterbedroom_Fan.sendCommand(1) }
etc.
if (MasterBedroom_Fan.state == 0) { Masterbedroom_Fan_Zooz.sendCommand(25) }
etc.
Now is when I belatedly tell you that I have very little training as a programmer, and someone else probably has a better solution. Iām just one of the few people still awake due to living on the west coast of Canada, so youāre stuck with me.
No, I definitely need to know which one has changed in my main loop, otherwise the non-changed one will just cancel out the changed one. Basically I only want to change the Item that has not changed, not both, otherwise I end up in an infinite loop, or at the very least immediately undoing what I just did.
Ah, I see what you mean now. I was previously going to suggest that you just use two separate rules for each device trigger, and I didnāt think that through when I revised to āone rule with many IF statementsā.
This is what I have so far, it appears to be working more or less ok, but I welcome suggestions on better ways to do this.
import java.util.concurrent.locks.ReentrantLock
val ReentrantLock lock = new ReentrantLock()
rule "master bedroom ceiling fan control"
when
// MasterBedroom_Fan is the iFan03
Item MasterBedroom_Fan changed or
//MasterBedroom_Fan_Zooz is the Dimmer
Item MasterBedroom_Fan_Zooz changed
then
// Prevent Concurrency from happening.
lock.lock()
try {
// If the change came form the MasterBedroom_Fan (ie the iFan03) then
// change the state of MasterBedroom_Fan (ie the Dimmer)
if (triggeringItem.state == MasterBedroom_Fan.state) {
switch (MasterBedroom_Fan.state) {
case 0 :
{
MasterBedroom_Fan_Zooz.sendCommand(0)
}
case 1 :
{
MasterBedroom_Fan_Zooz.sendCommand(33)
}
case 2 :
{
MasterBedroom_Fan_Zooz.sendCommand(50)
}
case 3 :
{
MasterBedroom_Fan_Zooz.sendCommand(67)
}
}
}
// If the change came form the MasterBedroom_Fan_Zooz (ie the Dimmer) then
// change the state of MasterBedroom_Fan (ie the iFan03)
else if (triggeringItem.state == MasterBedroom_Fan_Zooz.state) {
// If the dimmer is off, then set the Fan to 0 or off
if (MasterBedroom_Fan_Zooz.state == 0) { MasterBedroom_Fan.sendCommand(0) }
// If the dimmer is between 1 and 33% then set it to 1 or Low
else if (MasterBedroom_Fan_Zooz.state > 0 && MasterBedroom_Fan_Zooz.state <= 33) { MasterBedroom_Fan.sendCommand(1) }
// If the dimmer is between 34 and 66% then set it to 2 or Medium
else if (MasterBedroom_Fan_Zooz.state > 33 && MasterBedroom_Fan_Zooz.state <= 66) { MasterBedroom_Fan.sendCommand(2) }
// If the dimmer is between 67 and 99% then set it to 3 or high
else if (MasterBedroom_Fan_Zooz.state > 66 && MasterBedroom_Fan_Zooz.state <= 99) { MasterBedroom_Fan.sendCommand(3) }
}
}
finally
{
lock.unlock()
}
end
Right, that code worksā¦ mostly. Playing around with it I managed to get the rule to go into an infinite loop a few times (and cycle the status of both Items repeatedly, about 12 times/second), which I thought the lock was supposed to stop. Any suggestions on how to prevent this?
Would adding a 1 second pause before unlocking make a difference?
...
createTimer(now.plusSeconds(1),[])
}
finally
{
lock.unlock()
}
end
or
...
}
finally
{
createTimer(now.plusSeconds(1),[])
lock.unlock()
}
end
Drop the ReentrantLock (not needed) and use postUpdate instead of sendCommand for the MasterBedroom_Fan. Then change the trigger for MasterBedroom_Fan to āreceived commandā.
Wait, waitā¦ forgot these are both devicesā¦ is MasterBedroom_Fan ever manually adjusted at the device? If so, this may not work. But if you arenāt manually manipulating it, then this should work.
Itās a box hidden in the fan. It has a webUI that has buttons you can click to control it, but I suspect Iāll rarely if ever use those. And I actually was not even using it when I managed to drop into never ending loops, that happened when I adjusted it in the control section of PaperUI.