I have a very similar configuration.
I have a relay wired to the opener’s push button which toggles on and off upon receipt of a message on an MQTT topic. I have a reed switch that indicates the open/closed state of the garage door, again reported on a different MQTT topic.
The key in modeling this is to separate the actuator (i.e. the thing that causes the door to open/close) from the sensor (i.e. the thing that tells you whether the door is open or not) into separate Items. They really are two distinct things with distinct purposes so they really should be separated.
I have the relay represented in OH as a Switch Item but on the sitemap it is represented as a single button. Any command sent to this switch will generate a message to the relay topic and the controller simulates the button press on the opener (500 msec high, then back to low). I actually have yet another switch bound to the MQTT topics to send the actual messages because I have a bunch of stuff I want to do before the message gets sent in a rule.
I have the reed switch represented in OH as a separate Contact Item. In answer to your cosmetics question, it is documented in the Items wiki page. In short, the last part of the name of an Icon is how OH maps the state to the icon. So if you are using a Switch which can only be ON or OFF, it will only work with icons ending in on.png or off.png (e.g. light_on.png and light_off.png). Since the door icons end in open.png and closed.png they won’t work with a Switch (unless you make copies of them and rename to end in on.png and off.png).
When the door is opened externally to OH, only the reed switch triggers a message to OH and because the actuators and sensors are separate Items there is no looping problem.
On the sitemap I use the state of the Contact Item to turn on/off visibility of the switch based on the state of the contact so I can simulate the changing of the Open/Closed door icons and change the word on the button. NOTE: this trick doesn’t always work that well, particularly on the phones due to a problem with refreshes in OH 1.
My code (notes: I have two openers and I treat the garage door sensors just like all my other door sensors and I’ve posted my rules here as they exist in my file with some additional comments so there will be a bunch of extra stuff in the rules, let me know if you have questions. I also didn’t include all the groups as they are outside the scope of this conversation):
#Items
// Items to represent the door openers on the sitemap and receive commands
Switch T_D_Garage1 "Garage Door 1" <garagedoor> (gHistory, gMyOpenhab, gGarageOpeners)
Switch T_D_Garage2 "Garage Door 2" <garagedoor> (gHistory, gMyOpenhab, gGarageOpeners)
// Items to send the trigger command to the openers
Switch T_D_Garage1_MQTT "Garage Door 1" { mqtt=">[mosquitto:actuators/garage1:command:*:default]"}
Switch T_D_Garage2_MQTT "Garage Door 2" { mqtt=">[mosquitto:actuators/garage2:command:*:default]"}
// Items to represent the open/closed status of the opener
Contact N_D_GarageDoor1 "Garage Door 1 [MAP(en.map):%s]" <garagedoor> (gHistory, gDoorSensors, gRemindDoorSensors, gCerberosSensors) { mqtt="<[mosquitto:entry_sensors/main/garage/door1:state:default]" }
Contact N_D_GarageDoor2 "Garage Door 2 [MAP(en.map):%s]" <garagedoor> (gHistory, gDoorSensors, gRemindDoorSensors, gCerberosSensors) { mqtt="<[mosquitto:entry_sensors/main/garage/door2:state:default]" }
// Item to represent the online status of the garage controller (uses MQTT LWT messages as well as NH binding)
// The corresponding rules for these Items are not included
Switch N_C_Cerberos_mqttReporter "Cerberos MQTT Reporting Service [%s]" <network> (gRemoteSensors, gCerberosNet) { mqtt="<[mosquitto:status/sensor-reporters:command:OFF:.*cerberos sensorReporter is dead.*]" }
Switch N_C_CerberosRaspi "Cerberos Server" <network> (gCerberosNet) { nh="192.168.1.203" }
Garage Door Opening Rules
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import org.eclipse.xtext.xbase.lib.*
val Functions$Function1 openGarage = [ String garageNum |
logInfo("Garage Controller", "The Garage Door " + garageNum + " opener has been triggered")
// Send the command to the opener's MQTT Topic
switch garageNum {
case "1" : T_D_Garage1_MQTT.sendCommand(ON)
case "2" : T_D_Garage2_MQTT.sendCommand(ON)
default : logError("Garage Controller", garageNum + " is an unknown garage!")
}
// Alert if the opener controller is down
if(N_C_Cerberos_mqttReporter.state != ON) {
Notification_Proxy_Alarm.postUpdate("The garage controller may be offline, trigger may have failed!")
}
// Alert if the opener was triggered and no one is home.
if(Present.state != ON) {
Notification_Proxy_Alarm.postUpdate("Garage door " + garageNum + " has been triggered and no one is home!")
}
]
rule "Trigger Garage Door Opener 1"
when
Item T_D_Garage1 received command or
Item T_C_Minimote_1_Button1_Short changed to ON or
Item T_C_Minimote_1_Button1_Long changed to ON or
Item T_C_Minimote_2_Button1_Short changed to ON or
Item T_C_Minimote_2_Button1_Long changed to ON
then
openGarage.apply("1")
end
rule "Trigger Garage Door Opener 2"
when
Item T_D_Garage2 received command or
Item T_C_Minimote_1_Button2_Short changed to ON or
Item T_C_Minimote_1_Button2_Long changed to ON or
Item T_C_Minimote_2_Button2_Short changed to ON or
Item T_C_Minimote_2_Button2_Long changed to ON
then
openGarage.apply("2")
end
Garage Door Open Status Rules
import org.openhab.core.library.types.*
import java.util.Formatter
import java.util.Locale
import org.joda.time.*
import org.joda.time.format.*
import java.util.Map
import org.openhab.model.script.actions.*
// Lets me keep track of sent notifications so we don't send more than one per event
val Map<String,Boolean> openNotif = newHashMap
// The entry sensors will report the current status when it receives "update" on the update MQTT topic
rule "Get update at startup"
when
System started or
Item T_Update received command // used for debugging
then
logInfo("Alarm Controller", "Requesting an update from the MQTT sensors")
N_D_Update.postUpdate("update")
end
// A reed switch changed state
rule "A Door's State Changed"
when
Item N_D_Front changed or
Item N_D_Back changed or
Item N_D_Garage changed or
Item N_D_GarageDoor1 changed or
Item N_D_GarageDoor2 changed
then
// get the doors that changed in the last second
gDoorSensors.members.filter(door|door.changedSince(now.minusSeconds(1))).forEach[ door |
val StringBuilder msg = new StringBuilder
msg.append("The ")
var String name = door.name.split("_").get(2)
if(name.contains("Door")){
msg.append("Garage Door ")
msg.append(name.substring(name.length - 1))
}
else{
msg.append(name)
msg.append(" Door")
}
msg.append(if(door.state == OPEN) " was opened" else " was closed")
var alarm = false
// Generate alarm message if needed
if(Present.state != ON) {
msg.append(" and no one is home")
alarm = true
}
if(TimeOfDay.state.toString == "Night") {
msg.append(" and its night")
alarm = true
}
msg.append("!")
// Save the date/time for openings
if(door.state == OPEN) {
gDoorSensorsTime.members.filter(dt|dt.name == door.name+"_Last_Opened").head.postUpdate(new DateTimeType().toString)
}
// Note the opening and send alert
logInfo("Entry", msg.toString)
if(alarm) {
// Wait 30 seconds and check again before sending an alert if we are not home
if(msg.toString.contains("no one") && !msg.toString.contains("night")) {
try {Thread::sleep(30000)} catch(InterruptedException e){}
if(Present.state != ON) Notification_Proxy_Alarm.sendCommand(msg.toString)
}
// At night send the alarm immediately
else {
Notification_Proxy_Alarm.sendCommand(msg.toString)
}
}
// Reset the notification flag
openNotif.put(door.name, false)
]
end
rule "Reminder for doors that are open for over hour that we want to know any time"
when
Time cron "0 0/15 * * * ?"
then
gRemindDoorSensors.members.filter[s|s.state == OPEN && !s.changedSince(now.minusHours(1))]?.forEach[ door |
if(!openNotif.get(door.name)){
val String msg = door.name + " is still open after an hour!"
Notification_Proxy_Alarm.postUpdate(msg)
openNotif.put(door.name, true)
}
]
end
rule "Reminder for doors that are open for over an hour at night"
when
Time cron "0 0/15 * * * ?"
then
if(TimeOfDay.state.toString == "Night") {
gDoorSensors.members.filter[s|s.state == OPEN && !s.changedSince(now.minusHours(1))]?.forEach[ door |
if(!openNotif.get(door.name)){
val String msg = door.name + " is still open and its night!"
Notification_Proxy_Alarm.postUpdate(msg)
openNotif.put(door.name, true)
}
]
}
end
rule "Warning about doors when we leave the house"
when
//Item Present received command OFF
Item gRichPresent changed to OFF or
Item gJennPresent changed to OFF
then
// We don't want to wait the five minutes for Present to change
if(gRichPresent.members.filter[r|r.state == ON].size == 0 &&
gJennPresent.members.filter[j|j.state == ON].size == 0) {
gDoorSensors.members.filter(s|s.state == OPEN).forEach[door |
Notification_Proxy_Alarm.postUpdate("You left " + door.name + " open!")
openNotif.put(door.name, true)
]
}
end
#Sitemap
// Garage Door Openers
Switch item=T_D_Garage1 label="Garage Door 1" icon="garagedoor-closed" mappings=[ON=Open] visibility=[N_D_GarageDoor1!="OPEN"]
Switch item=T_D_Garage1 label="Garage Door 1" icon="garagedoor-open" mappings=[ON=Close] visibility=[N_D_GarageDoor1=="OPEN"]
Switch item=T_D_Garage2 label="Garage Door 2" icon="garagedoor-closed" mappings=[ON=Open] visibility=[N_D_GarageDoor2!="OPEN"]
Switch item=T_D_Garage2 label="Garage Door 2" icon="garagedoor-open" mappings=[ON=Close] visibility=[N_D_GarageDoor2=="OPEN"]