One item connected to multiple (modbus data) things: Best practice/Design Pattern wanted


I’m currently setting up OpenHAB for Home Automation with a Wago PLC. The PLC is integrated with the Modbus Binding over Modbus TCP. I have already a working setup that enables me to read/write coils/register in my PLC, so communication already works fine.

Next step is to integrate a simple light to OpenHAB controlled by the PLC. This light can also be switched on/off by a wall mounted switch also attached to the PLC, so the state of the light can change at any time (and OpenHAB should always know the actual and not any predicted state).

Therefore I provide 3 Modbus variables in the PLC that can be read/written by OpenHAB:

  1. get the actual light state (read by OpenHAB)
  2. switch light on (written by OpenHAB)
  3. switch light off (written by OpenHAB)

What I have in OpenHAB:

3 Things to read/write the above modbus variables

Bridge modbus:tcp:wago_t "Bridge Wago-T" [ host="", port="502", id=1 ] {
    // read-only for discrete inputs
    Bridge poller discreteInputs [ start=6144, length=1, refresh=500, type="discrete" ] {
        Thing data OH_R031_LichtStatus [ readStart="6144", readValueType="bit" ]

    // Write-only entry: thing is child of tcp directly. No readStart etc. need to be defined.
    // Note that the openHAB state might differ from the physical slave since it is not refreshed at all
    Thing data OH_R031_LichtAn [ writeStart="0", writeValueType="bit", writeType="coil", writeMultipleEvenWithSingleRegisterOrCoil="true" ]
    Thing data OH_R031_LichtAus [ writeStart="1", writeValueType="bit", writeType="coil", writeMultipleEvenWithSingleRegisterOrCoil="true" ]

and 3 switch items (each bound to the corresponding modbus data thing):

Switch SW_031_LichtStatus {channel="modbus:data:wago_t:discreteInputs:OH_R031_LichtStatus:switch"}
Switch SW_031_LichtAn {channel="modbus:data:wago_t:OH_R031_LichtAn:switch"}
Switch SW_031_LichtAus {channel="modbus:data:wago_t:OH_R031_LichtAus:switch"}

What I want:

Just one combined switch item that is bound to the 3 data things and which shows the actual state of the light and is also able to switch the light on and off.

I found out that the design pattern “Proxy item” by @rlkoshak may suite to my Problem, but doing that will mean that for about 50 lights in the building I’ll have a lot of monkey work to do (e.g. creating 50 rules for the proxy items). So I wonder if there is a simpler way to achieve this? Isn’t there a more generic way to group multiple things and link them to one item? Or is this problem some kind of PLC specific, because other bindings usually combine state and on/off commands already in the thing layer to one thing and modbus binding doesn’t do this? Maybe I’m just missing another layer between things and items…


A “normal” way to represent say a lighting relay, would be as a single Modbus coil register.
openHAB can read-poll the coil to see what state it is in.
openHAB can write on or off commands to the coil, to change it’s state.
The PLC can do it’s own thing with the coil, say with a timer, or in your case in response to a wall switch. openHAB will “find out” about that as it read-polls the coil.

There are oddball PLCs that require write and read to different coils, the binding will support that.

I use a proxy item for this. I poll the read register with the light status and update the proxy item. If I update the proxy item with OH it will update the write register on the device, if needed.


Contact ADAM6050_00_DI00            "Light [%s]"    (gADAMDigitalPInputForProxy) // Input with light status from modbus
Switch ADAM6050_00_DI00_Proxy       "Light [%s]"    (gMAPDB,gADAMDigitalProxy)          { autoupdate="false" } // proxy item which is used to controll/see what happens
Switch ADAM6050_00_DO00             "Light [%s]" //write to MODUS to change light status to on


// triggers on sendcommand but not on postupdate! this is important to know
// I can now use postupdate to the proxy item without causing this rule to fire
rule "Modbus DIxx_Proxy - needs change"
        Member of gADAMDigitalProxy received command
        // get status of related DI
        val ContactItem RelatedContact = ScriptServiceUtil.getItemRegistry?.getItem( + "DI" +,2)) as ContactItem

        // if DI is already what it should, do nothing
        if(receivedCommand == ON && RelatedContact.state == OPEN)
            sendCommand( + "DO" +,2), "1")

        else if(receivedCommand == OFF && RelatedContact.state == CLOSED)
            sendCommand( + "DO" +,2), "1")

This one will make the sync between the real DI and the proxy item

rule "Modbus DIxx - ELTACO Proxy ueberwachen und loggen"
        Member of gADAMDigitalPInputForProxy changed
        // if DI is triggered, set related proxy item
        if(previousState == NULL)

        postUpdate( + "_Proxy", if(triggeringItem.state.toString == "OPEN") "OFF" else "ON")

Please don’t do this. You need only create one Rule that can handle all of the Proxy Items. See Design Pattern: DRY, How Not to Repeat Yourself in Rules DSL for how to avoid that.

If rossko57’s approach doesn’t work then the follow Profile might work. But given you have channels that are supposed to be read only and others that are supposed to be write only I’m not sure if it would work.

No, there isn’t. The base assumption is that a given Channel represents a single control point on a device. It is an unusual case to have two or three Channels for a single light so there really isn’t anything built into OH to make handling that use case easier.

To clarify some here - there are a lot of oddball Modbus devices out there. Some will have you reading switch state from one place, and writing on and off commands to different places.

The OH Modbus binding v2 does have the flexibility to do such non-standard things. (Which also has folk complaining about complex setup😀)

Read and write targets are defined separately in the data thing. Complex write actions can be controlled with a javascript. There are examples on the docs.

But if you are programming your own PLC, don’t do it the oddball way unless you have to.