UDP Binding confusion

I’m trying to configure OpenHab to send “D:1A02:E” as a UDP packet to a server. 1 means one, A is a house code and 02 is a channel.

I have he following in tcp.items

Switch Switch_A1 “SwitchA1” (bbsb) { udp=">[ON:192.168.1.15:53008:‘D:1A02:E’], >[OFF:192.168.1.15:53008:‘D:0A02:E’]" }

but I’m getting the error ‘given transformation function ‘D:1A02:E’ does not follow the expected pattern ‘()’’

as it’s trying to use it as a transformation (even though I’m sending the packet).

I’m not really sure what I should do to fix this? I suspect I might have to use a map function but I don’t really know.

Also is there any way to use variables in the items definition - for example do I need to hard code the server ip on each line?

Ta

Ross

From reading the wiki it almost looks like it requires you to use a transform.

Try creating a .map file (lets call it my.map for this example) with the following as contents:

ON=D:1A02:E
OFF=D:0A02:E

Then your switch would read:

Switch Switch_A1 "SwitchA1" (bbsb) { udp=">[ON:192.168.1.15:53008:'MAP(my.map)'], >[OFF:192.168.1.15:53008:'MAP(my.map)']" }

NOTE: I didn’t verify I typed the above correctly. Could have a typo or mistake in it.

I think you are correct. Just means for every socket I have I will need to create two maps for it. I can’t imagine that can be correct!

Well, with excel I created the following:

with the last column having this formula

="Switch_" & UPPER(A2&B2&"_"&D2) & "=D:"&C2:C2&UPPER(A2)&TEXT(B2,"00")&":E"

so the mapping file has a lot in now, but odd that this is the way to do it!

Thanks for your help!

It looks like I will need a map file for every switch. Thats just not going to work for me!

I think what is needed is either

  1. a compatible extension to the TCP/UDP binding’s outbound binding string format, or
  2. a real or pseudo transform called ‘COPY(D:1A02:E)’ to simply copy the input to the output.
1 Like

First of all you should only need one map file per switch if you use this approach, not two. The same map file can support both ON and OFF.

Second of all, perhaps a Switch Item isn’t the right Item type for what you want. It is possible to have a Switch element on the sitemap that is mapped to a String or Number Item type and then use the Sitemap mappings feature (not to be confused with the Item mapping) to set it to the right value.

NOTE:I’m just typing in the below, there is almost certainly a type.

Items:

String Switch_A1 "SwitchA1" (bbsb) { udp=">[192.168.1.15:53008:'REGEX(.*)'] }

Sitemap:

Switch item=Switch_A1 mappings=["D:1A02:E"="On", "D:0A02:E"="Off"]

I’m not 100% this will work as written. I’ve only ever done this sort of mappings with Number Items, not String Items so I don’t know if you need the quotes to the left of the “=” in the mappings. Also, I think this will put two buttons on your sitemap instead of the one toggle switch.

Another approach can be to use a proxy switch and a rule.

Items:

String Switch_A1 "SwitchA1" (bbsb) { udp=">[192.168.1.15:53008:'REGEX(.*)'] }
Switch Switch_A1_Proxy "SwitchA1"

Rule:

rule "Switch_A1 Toggled"
when
    Item Switch_A1_Proxy received command
then
    if(Switch_A1_Proxy.state == ON) Switch_A1.postUpdate("D:1A02:E")
    else Switch_A1.postUpdate("D:0A02:E")
end

Sitemap:

Switch item=Switch_A1_Proxy

With this second approach your switches will appear on your sitemap as the old familiar toggles. You can also use groups and other coding tricks to consolidate all your rules into one if the mappings between the string and the switch follows some sort of pattern that allows you to generate the string that goes out,which is what it looks like with your excel spreadsheet.

Give me a bit and I’ll try to come up with something clever to consolidate so you only need the name of the switch triggered to generate the string that gets posted.

1 Like

Agreed, either would work well (the overload is neater, but given both are strings might be tricky)

I suppose you could also check for a transform, and if it’s not found just treat it as a string.

Wow, thanks for all your help. It’s difficult to know if you are just missing something obvious in the configuration so the help from the community is really appreciated!

OK, here is an attempt at a generic approach.

Requirements:

Persistence is setup and configured to save at least every change on the Switch items

Assumptions:

It is OK for the switches on the end of the socket to receive more than one command for each switch change (i.e. when you toggle a switch ON the other end of the socket will receive multiple “D:1A01:E” UDP packets. If not additional code will be required to handle a limitation that causes a rule triggered by a Group Item’s update to trigger multiple times for one update to a member’s state, or the rule trigger will need to be changed to trigger on the individual switches instead of the group.

All switches are controlled by the same destination IP address. If not you need to add additional String Items and logic to determine which String to postUpdate to for a given Switch.

Switches will always be toggled more than 200 milliseconds or more apart. The logic below cannot handle simultaneous toggling of switches.

Items:

Group gMySwitches
String Switch_Publisher { udp=">[192.168.1.15:53008:'REGEX(.*)'] } // assumes all switches go to the same address, the REGEX(.*) should just pass through the String unchanged

String Switch_A01 "SwitchA1" (gMySwitches) // notice the A01, this makes it easier later in the rule to construct the command string
String Switch_A02 "SwitchA2" (gMySwitches)
....

Rules:

rule "gMySwitch toggled"
when
    Item gMySwitch received update
then
    // Get the switch that was toggled
    Thread::sleep(200) // give persistence time to save the update
    var mostRecent = gMySwitch.members.sortBy[lastUpdate].last as SwitchItem // get the most recent switched item

    // Build the command string
    val StringBuilder cmd = new StringBuilder
    cmd.append("D:")
    if(mostRecent.state == ON) cmd.append("1") else cmd.append("0")
    cmd.append(mostRecent.name.split("_").get(1)) // get the A01 part of the switch name
    cmd.append(":E")

    // Issue the command string to the socket
    Switch_Publisher.postUpdate(cmd.toString)
end

Thanks for this, I’ll need to configure persistence to test it, but the code makes sense. Two questions

If I use a rule to turn off all switches will I need to add a 200 millisecond sleep between switches?

And what in that code causes the command to be sent multiple times (multiple times is actually a good thing as it’s probably needed anyway since the first one is sometimes missed).

Thanks!

At first I thought the answer was no but I realize now that the answer is actually yes, unless you make some changes to the above rule. The problem is you will want you Switch Items to be in sync with what the devices actually are so we can’t bypass the gMySwitch updates.

However, if you change your rule trigger in the above to

when
    Item Switch_A01 received command or
    Item Switch_A02 received command or
    ...

then if you just send the command string for each switch directly and postUpdate to the switches (which will not trigger the rule above) then the answer is no. You could something like:

gMySwitches.members.forEach[s |
    val StringBuilder cmd = new StringBuilder
    cmd.append("D:0")
    cmd.append(s.name.split("_").get(1)) // get the A01 part of the switch name
    cmd.append(":E")
    Switch_Publisher.postUpdate(cmd.toString)
    s.postUpdate(OFF) // postUpdate will not cause the rule to trigger
]

And if you wanted to reuse this command generation and publishing code in both rules you can create a lambda.

val Functions$Function3 cmdSwitch = [ String name, OnOffState st, StringItem publish |
    // Build the command string
    val StringBuilder cmd = new StringBuilder
    cmd.append("D:")
    if(st == ON) cmd.append("1") else cmd.append("0")
    cmd.append(name.split("_").get(1)) // get the A01 part of the switch name
    cmd.append(":E")

    // Issue the command string to the socket
    publish.postUpdate(cmd.toString)    
]

Then your original rule would replace everything after // Build the command string with

cmdSwitch.apply(mostRecent.name, mostRecent.state, Switch_Publisher)

And the code above to turn them all off would become:

gMySwitches.members.forEach[s |
    cmdSwitch.apply(s.name, OFF, Switch_Publisher)
    s.postUpdate(OFF) // postUpdate will not cause the rule to trigger
]

This lets you consolidate the code that creates the command string into one place.

It is a side effect of how the state of a Group is calculated. What ends up happening (I’m making an educated guess here, I haven’t actually looked at the code yet) is when a member of a Group receives an update, the Group gets updated. BUT, then the Group checks all the other members to recalculate the Group’s own state (remember, the state of a Group is an aggregate of the states of all of its members). So for each of the other members it calculates a new state which results in an update to the Group for each member. So when you have a rule that is triggered by a Group’s update, it will actually trigger the rule once for each member of the group.

Most of the time I’ve found this doesn’t really matter but I wanted to make you aware of this behavior.

I’m throwing a lot of advanced stuff at you(lambdas, behaviors of Group updates, difference between command and update, etc). It took me months to learn some of these tricks so don’t feel bad if you are overwhelmed. This was a fun problem to work out though. I hope it helps.