Rule based http binding

Hi,

I target a crisp rule setup to control multiple http connected lights of same type.
Simple rules with Lambda usage took me half the way,
but my current setup requires the light hostnames listed in the rules.
I would prefer to extract it from the http binding configuration, but unsure how to.

Maybe you can share an idea?

Below is what I got so far. Cheers Chris

items

Number   L1   "Light 1"   { http=">[CHANGED:POST:http://hostname1/]" }
Number   L2   "Light 2"   { http=">[CHANGED:POST:http://hostname2/]" }

sitemap

Switch   item=L1   mappings=[3="1",2="+",1="-",0="0"]
Switch   item=L2   mappings=[3="1",2="+",1="-",0="0"]

rules

// lambda expression that can be used as a function (here: with 3 parameters)
val org.eclipse.xtext.xbase.lib.Functions$Function3 Basics = [
    org.openhab.core.library.items.NumberItem numberItem,
    int receivedCommand,
    String hostname |
    
    var String cmd
    	
    switch receivedCommand {
        case 3:    { cmd="cmd1" } // On
        case 2:    { cmd="cmd+" } // +
        case 1:    { cmd="cmd-" } // -
        default:   { cmd="cmd0" } // Off
    }

    sendHttpPostRequest("http://"+hostname+"/"+cmd)
]

rule "Light 1 Basics" when L1 received command then
    Basics.apply(L1, receivedCommand, "hostname1") end
rule "Light 2 Basics" when L2 received command then
    Basics.apply(L2, receivedCommand, "hostname2") end

I’m confused. Why do you have http binding config on L1 and L2? My reading of this is that when L1 receives a command it will send a POST to http://hostname1/ with a body of “1”, “+”, “-”, or “0” followed by your call to sendHttpPostRequest with the proper URL.

I think the following would work and you can eliminate your rules entirely:

Number L1 "Light 1" { http=">[CHANGED:POST:http://hostname1/cmd%2$s" }
Number L2 "Light 2" { http=">[CHANGED:POST:http://hostname2/cmd%2$s" }

The binding gives you access to two dynamic variables when defining the URL. 1 is the current datetime and 2 is the Item’s current state. % means “use a dynamic variable”. 2$ means use the Items’s state. And s means as a String.

See the Dynamic URLs section of the HTTP binding wiki page.

To expand on Rich’s answer:

items (react on commands, not when the state changes, and pass the string directly)

String L1 "Light 1" { http=">[*:POST:http://hostname1/cmd%2$s]" }
String L2 "Light 2" { http=">[*:POST:http://hostname2/cmd%2$s]" }

sitemap (send the string commands that are what gets appended to the URL)

Switch   item=L1   mappings=["1"="On","+"="+","-"="-","0"="Off"]
Switch   item=L2   mappings=["1"="On","+"="+","-"="-","0"="Off"]
1 Like

Thanks John & Rich.

I tried also the dynamical part, but unfortunately “cmd*” is not that simple, more of the format

"Reg?param1=value1+param2=val2+..."

I assume I could expand “mappings” to the above Syntax,
but then I would need to define it for each item and
have no common code to adjust the mapping for all related items at once.

If your Switch widgets are setup to turn lights on, brighter, dimmer and off, do these differ per item? Note that %2$s will be the string version of the command, as defined in the mappings, and that can be embedded in the URL wherever it’s needed. Yes, there might be a copy/paste per item and per widget, but it should work, no?

All the solutions are working, it’s more about config code structure & maintainablility.

On one hand I’ll need think about easier commands to fit better in the “mappings”.

But allow me two question:

  • Is there any way to read out the mappings string from a rule?
  • May I use more complex wildcards (e.g. “reg?param=val”) than only ‘*’ in the command section of mappings?

No official way. One could write a pile of code to parse the sitemap in a rule; not a good idea generally.

* means “match any command”, but I’m pretty sure you could put any command string X where the * appears in the “out” binding string, meaning, “when the item is sent the X command, perform the following HTTP request.” You might have trouble if the string command contains a colon (:) due to parsing. It would be a deficiency in the HTTP binding if you were limited to a subset of possible commands, and hence worth a new issue in the openhab/openhab repo.

Thanks, I’ll try extended matching together with adjustment of http commands.
That could help to limit the complexity of mappings. maybe of few classes with different Parameter usage.

Update:
I added a string item per light and kept the rule to map between number and string item.
Sitemap only uses number items and rule becomes a kind of “hardware driver”.
Your proposals for dynamical http usage are applied to the string item.

Pros

  • hostname stored in items, simple http binding & sitemap mapping
  • common mapping rule for items of same type
  • rule to cover also more complex operations like color picking / temperature selection

Cons

  • one additional string item per device

items

Number   L1   "Light 1"
String   L1_S "Light 1"   { http=">[*:POST:http://hostname1/%2$s]" }
Number   L2   "Light 2"
String   L2_S "Light 2"   { http=">[*:POST:http://hostname2/%2$s]" }

sitemap

Switch   item=L1   mappings=[3="1",2="+",1="-",0="0"]
Switch   item=L2   mappings=[3="1",2="+",1="-",0="0"]

rules

// lambda expression that can be used as a function (here: with 3 parameters)
val org.eclipse.xtext.xbase.lib.Functions$Function3 Basics = [
    org.openhab.core.library.items.NumberItem numberItem,
    org.openhab.core.library.items.StringItem stringItem,
    int receivedCommand |
    
    var String cmd
    	
    switch receivedCommand {
        case 3:    { cmd="cmd1" } // On
        case 2:    { cmd="cmd+" } // +
        case 1:    { cmd="cmd-" } // -
        default:   { cmd="cmd0" } // Off
    }

    stringItem.sendCommand(cmd)
]

rule "Light 1 Basics" when L1 received command then
    Basics.apply(L1, L1_S, receivedCommand) end
rule "Light 2 Basics" when L2 received command then
    Basics.apply(L2, L2_S, receivedCommand) end
2 Likes