Parsing an MQTT message in rules

Tags: #<Tag:0x00007f617996d930>

Hi , I am new to openHab I have this question

So I have a controller that has 8 outputs it can only send the status of all ports
combined together, Example if it sends 255 it means All 8 ports are ON just
convert the number to binary and can see the status of the ports,
My question is this, how would a rule look like to update switches status in openhab from that number? if it is possible to do.

Messing with bitpacked messages in Rules DSL is a pain. What Rules are you using, Rules DSL or Scripted Automation (Python or JavaScript)?

Hi rlkoshak ,

Thanks for your reply, I think is DSL ( the default language ) , the controller sends MQTT message like PA=255 cos it has 4 ports PA,PB,PC,PD what i have done till now is a string item that displays this data
now what i need to do is some string manipulation in the rules for example:
when string change , check if string contains “PA=” if yes extract the number after "PA= "
then example i have a value of 255 convert that in to a binary string of 8 characters , then
pic the character i am interested in check if 1 or 0 , if 1 then the switch is ON if 0 put it off
, but as i said i am new to dsl i know some cpp and php but never dsl ,

also what programming languages are accepted in the rules files ?


val checkKthBit = [ Integer num, Integer bit |
    val n = num.intValue
    val k = bit.intValue

    if ((n & (1 << (k - 1))) == 1) True else False // shift to the kth bit and check if it's 1
]

if(checkKthBit.apply(MyItem.state, 0)) // device from first bit is ON
if(checkKthBit.apply(MyItem.state, 1)) // device from second bit is ON
...

I am not at all certain that the above will work. I think Xtend will let us use the Java binary shift operators. If not I’m not sure how to do this.

If you use Scripted Automation you can use Python (specifically Jython 2.7), JavaScript (specifically Nashorn), or Groovy. See [beta testers wanted!] Jython addon w/ helper libraries (requires OH 2.5.x) to get started with Python.

1 Like

Thanks very much for your help,
I will try it and let you know that would be tomorrow cos now here is 12:33 AM :slight_smile:

thanks again Good day

Hi , I have started to experiment some code with regex , when i test the regex on simulator it
it works but in rules file dose not , what am i doing wrong ?
I want to extract only whats after “PA=” in item PORTA

val str1 = transform(“REGEX”, “(?<=PA=).*”, PORTA.state.toString)

also how you convert string to int please ?

In OH, the REGEX must match the entire string and the first matching group is what gets returned.

1 Like

Hi Rich, thanks for your reply.

it seems that in my case regex is useless cos if I know the whole string i wouldn’t be needing it.
but I am a beginner maybe I am missing something.

Python seems to be popular and a lot of help is available on the web will try to see how I can use it
to create rules.

also, I know you suggested a number item but because the controller sends all data in the same topic
inspecting the string is the only way I have to distinguish the data.

Consider if you know (or want to see if it is present) “wildcard - magicword - wildcard”. That’s the whole string.

1 Like

Thanks Rossko ,

it finally worked like you suggested:
reg

but it’s a long way to victory i have to convert str1 to int and figure out how checkKthBit is going to work
is checkKthBit part of the standard DSL or i have to install it? like transform Regex

That’s Rule code that you will need to copy and paste into a Rule and very likely debug it as I have no way to really test that it works as written above.

1 Like

Once you have the value stored in an integer you might want to implement something like this in the rule instead of checkKthBit.

Items:

Switch      Port8       "Port 8" 
Switch      Port7       "Port 7" 
Switch      Port6       "Port 6" 
Switch      Port5       "Port 5" 
Switch      Port4       "Port 4"   
Switch      Port3       "Port 3" 
Switch      Port2       "Port 2" 
Switch      Port1       "Port 1"

Rule:

//  AllBits must be defined as an integer.  The value from MQTT will be store here
var Integer AllBits  

when ???? changed 
then
    // Place the value from the REGEX in AllBits (possibly converting to an integer beforehand)
    
    if (AllBits >= 128 ) {
        Port8.postUpdate(ON)
        AllBits = AllBits - 128
    }
    else {
        Port8.postUpdate(OFF)
    }
    if (AllBits >= 64 ) {
        Port7.postUpdate(ON)
        AllBits = AllBits - 64
    }
    else {
        Port7.postUpdate(OFF)
    }
    if (AllBits >= 32 ) {
        Port6.postUpdate(ON)
        AllBits = AllBits - 32
    }
    else {
        Port6.postUpdate(OFF)
    }
    }if (AllBits >= 16 ) {
        Port5.postUpdate(ON)
        AllBits = AllBits - 16
    }
    else {
        Port5.postUpdate(OFF)
    }
    if (AllBits >= 8 ) {
        Port4.postUpdate(ON)
        AllBits = AllBits - 8
    }
    else {
        Port4.postUpdate(OFF)
    }
    if (AllBits >= 4 ) {
        Port3.postUpdate(ON)
        AllBits = AllBits - 4
    }
    else {
        Port3.postUpdate(OFF)
    }
    if (AllBits >= 2 ) {
        Port2.postUpdate(ON)
        AllBits = AllBits - 2
    }
    else {
        Port2.postUpdate(OFF)
    }
    if (AllBits >= 1 ) {
        Port1.postUpdate(ON)
        AllBits = AllBits - 1
    }
    else {
        Port1.postUpdate(OFF)
    }
end

I haven’t tested this but VSC shows no syntax errors. Its not as elegant as checkKthBit but easier to understand (IMO)

1 Like

HI thanks to All, for your reply’s to Help.

Yes, it seems that your approach Jim is more suitable for a beginner I can clearly understand what’s happening and will surely implement that.

Once I feel more fluent in this language will also try checkKthBit which really looks elegant.
searching the forum was not able to find a simple syntax to convert a string to int.

In c++ i use to do it :

int = stoi (string) ;

is there something simple like this in DSL?

like

1 Like

Hi Rosko, yes it worked regex converted it into an integer

NumberOutput = new Integer(transform(“REGEX”, “([0-9]+)”, StringInput))

var String X_Topic = "uart_rx"
var String Broker = "mqtt:systemBroker:embedded-mqtt-broker"
var String str1 = "" 
var int AllBits = 0


rule XC1_PA_UPD
when
	Item PORTA received update
then
   
   str1 = transform("REGEX", ".*(?<=PA=)(.*)", PORTA.state.toString) // remove PA= & put value in to str1
   AllBits = new Integer(transform("REGEX", "([0-9]+)", str1))  // Convert String to int
   PAVAL.sendCommand(AllBits)  // Update PAVAL 
	val mqttActions = getActions("mqtt", Broker) mqttActions.publishMQTT(X_Topic, "|ssd0clr|SSD0:X1,Y1,F0,T1,S[" + str1 + "]|") // Print received data on LCD. 
   
                                       // Update Switches Status //
if (AllBits >= 128 ) { PA8.sendCommand(ON)  AllBits = AllBits - 128 } else { PA8.sendCommand(OFF) }
if (AllBits >= 64 )  { PA7.sendCommand(ON)  AllBits = AllBits - 64 }  else { PA7.sendCommand(OFF) }
if (AllBits >= 32 )  { PA6.sendCommand(ON)  AllBits = AllBits - 32 }  else { PA6.sendCommand(OFF) }
if (AllBits >= 16 )  { PA5.sendCommand(ON)  AllBits = AllBits - 16 }  else { PA5.sendCommand(OFF) }
if (AllBits >= 8 )   { PA4.sendCommand(ON)  AllBits = AllBits - 8 }   else { PA4.sendCommand(OFF) }
if (AllBits >= 4 )   { PA3.sendCommand(ON)  AllBits = AllBits - 4 }   else { PA3.sendCommand(OFF) }
if (AllBits >= 2 )   { PA2.sendCommand(ON)  AllBits = AllBits - 2 }   else { PA2.sendCommand(OFF) }
if (AllBits >= 1 )   { PA1.sendCommand(ON)  AllBits = AllBits - 1 }   else { PA1.sendCommand(OFF) }

end

This DSL language … lest just say I don’t like it at all, there must be a better way.
Without your help guys, I would have given up so thanks very much.

As I indicated above, there is the option to use Python, JavaScript or Groovy.

1 Like

Hi Rich,

Yes i will do that , is it done via the Exec Transformation Add-on ?

You can call external scripts using the Exec binding or from Rules DSL Rules using the executecommandLine Action, but that’s not what I’m referring to. I’m referring to writing openHAB Rules using these other languages instead of Rules DSL.

See [beta testers wanted!] Jython addon w/ helper libraries (requires OH 2.5.x) to get started with Python.

Just for completeness I was able to get the checkKthBit routine working in a .rules file.

// The variable "num" must be between 0 and 255.
// The variable "bit" must be between 1 (the rightmost bit) and 8 (the leftmost bit)
val checkKthBit = [ Integer num, Integer bit |
    var n = num
    var s = 1 << (bit - 1)
    if (n.bitwiseAnd(s) > 0) {
       true
    } 
    else {
       false
    }
]

Assuming AllBits is a number item

   if (checkKthBit.apply(AllBits.state,8)) {
        Port8.postUpdate(ON)
    }
    else {
        Port8.postUpdate(OFF)
    }
    if (checkKthBit.apply(AllBits.state,7)) {
        Port7.postUpdate(ON)
    }
    else {
        Port7.postUpdate(OFF)
    }
....
....
2 Likes

Hi Jim .

Yes i can confirm it works 100% Thanks :+1: