Data from python script

I have a problem with Modbus binding and power meter which I described here:

I gave up with idea of usage of modus binding and have a plan to deploy python script. Problem is that I want to import about 10 variables and have no good idea how to do this…

Now I am trying to prepare 10 python scripts and import one by one. I have tested exec binding and it is working well, but I had a conflict with my other modbus device (when update is same time).

II have decided to write a rule which frequently will update my items (just after mu other modbus refresh) and stugged with data conversion.
I know that there was a lot, but none od solution seems to be working :frowning:

var Number result = (executeCommandLine("python  /home//VL1.py",500))
logInfo("INFO", result)
var Number result2 = Double::parseDouble result
if (result2 > 0)
	logInfo("Rules", result2)

I got en error during data conversion. So how to achieve similar result as in items declaration (then string to number is done automatically)
The result of VL1.py is a number such as: 221.1
Can I simple convert such string to a number/float without searching and removing “.” ?

Linux will prevent more than one program from writing to the same socket at the same time. If you have a binding that is already using that device, your Python scripts will not be allowed to write to it too.

So what you are trying to do will ultimately probably not work. You either need to use the other Modbus binding that is doing the refresh or just the Python scripts. Not both.

But assuming it would work:

var Number result = (executeCommandLine("python  /home//VL1.py",500))

should be

var Number result = new BigDecimal(executeCommandLine("python  /home//VL1.py",500))

executeCommandLine only returns a String. You have to convert it to a Number before you can save it to a Number variable.

Or you need to change it to

var String result = executeCommandLine("python  /home//VL1.py",500)

This line is also completely wrong

var Number result2 = Double::parseDouble result

You seem to not understand the proper use of parenthesis. You use parens around the arguments to a method call. So above the parens you had around executeCommandLine("python /home//VL1.py",500) was wrong and your lack of parens on this line is wrong.

var Number result2 = Double::parseDouble(result)

Yes, this I know, so that why I decide to do this as a rule, not as assignment in item declaration (via exec). I have idea to trigger a rule via updated item from my other modbus device. So in example all items update every 30s, then when it is finished my rule will launch python script to get connect via second device.

This seems to not working, I got an error.
What exactly this declaration means ? That read string will be convert to big decimal ?

First line of course works, but then I got an error:

java.lang.IllegalStateException: Could not invoke method: org.openhab.model.script.actions.LogAction.logInfo

It looks like conversion to a number does not work …

That’s correct :frowning_face:, I have programming on my studies … 15 years ago. I know/remember some general ideas (ie C/C++) but I have a problem with Java from time to time like this data conversion:roll_eyes:.

Thank you for your help !!

It is hard to help if you don’t say what the error is.

Yes and BigDecimal is a Number. It is a better approach to converting a String to a Number than parseDouble.

That error has nothing to do with those lines of code. It is complaining about a logInfo statement. So there is something going on with one of your log statements. Try logInfo("INFO", ""+result2). This is one of the reasons why creating a BigDecimal is better than parseDouble. parseDouble gives you a primitive and the Rules DSL (see below) isn’t very good at working with primitives.

While you can access Java classes from Rules, Rules are not written in Java. They are written in a Domain Specific Language based on the Xtend Language.

You would be served to use the ESH Designer for now and soon move to the VSCODE editor once syntax checking is implemented (should be soon). It will point out the errors as you type. You have to be careful with ESH Designer though as it is way behind OH and there are lots of new features that it does not support and will therefore mark as an error. In particular, Items created in PaperUI, non-default Actions, and Channel Triggers will be falsely called an error.

So, this declaration:

var Number result = new BigDecimal(executeCommandLine("python /home/VL1.py",500))

caused:

2017-11-30 22:09:40.061 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Import PowerMeter
java.lang.NullPointerException: null
	at org.eclipse.xtext.common.types.util.JavaReflectAccess.getRawType(JavaReflectAccess.java:107) ~[na:na]
	at org.eclipse.xtext.common.types.util.JavaReflectAccess.getConstructor(JavaReflectAccess.java:90) ~[na:na]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateConstructorCall(XbaseInterpreter.java:511) ~[na:na]
	at sun.reflect.GeneratedMethodAccessor581.invoke(Unknown Source) ~[na:na]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
	at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]

String +convertion, seems to be working:

var String result = executeCommandLine("python  /home/VL1.py",500)
logInfo("INFO", result) 
var Number result2 = Double::parseDouble(result)

result:

2017-11-30 22:16:20.415 [INFO ] [org.openhab.model.script.INFO ] - 227.0

I had a problem with “log”, but when add “” solution with parse works, problem is only first idea. Do you know what is wrong ?

Thanks again !

The error probably indicates that the result of the executeCommandLine cannot be parsed into a Number but if that where the case your ::parseDouble would also fail. I’m not sure what is going on but you can use the same code as parseDouble by using:

var Number result = new Decimal(executeCommandLine...

Just checked in designer, which wrote:
_Couldn't resolve reference to JvmConstructor 'BigDecimal'_

Do I need to import some specific library ?

I use:

import org.openhab.core.library.types.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import org.openhab.core.persistence.*
import org.joda.datetime.*
import java.time.LocalDateTime
import org.openhab.core.types.*
import java.lang.Integer
import java.lang.Double
import java.lang.String

Also, when I put string as argument (I’m not sure of I can do this) I have same error. ie:
var Number result = new BigDecimal("223.3")

No, this must be one of the places where ESH Designer is deficient. That is non-error.

BTW, if you are running OH 2 you need none of those imports. If you are running OH 1.x you should upgrade, and you don’t need to import anything from java.lang (which is where BigDecimal resides).

A little late to suggest this as you might be nearly there with help from Rich in solving your data conversions but did you consider you could write 1 python script that pushes the data in to OH rather 10 scripts pulling via return results?

There’s an openhab python module available for import:

sudo pip install python-openhab

Then in your python code you need an import:

from openhab import openHAB

initialise:

base_url = 'http://localhost:8080/rest
openhab = openHAB(base_url)

and then use as you want:

myItem = openhab.get_item(‘sen_Lock_Front_Door’)
myItem.state = “OPEN”

https://pypi.python.org/pypi/python-openhab/2.2

Same result / error as BigDecimal.
Anyway I will use second solution.

Thanks Ray !

Of course one script is better then few smaller :slight_smile:, so I can edit it, but I have lack of idea how to send more then one data to openhab. Script looks good, but I can not get a concept.
As I understand this script will publish something on http and then I can import data for openhab ?
Or python script will update openhab’s items ?
To be clear I want to read register and push them openhab’s items (via python script).

The concept is to execute the python script once and it will read all the data and push it straight in to OH via the openhab python module. Return result of the script would most likely just be a normal error response.

In Python you just need to repeat bits like this to directly update as many OH items as you like:

item = openhab.get_item(‘OH_Voltage_1’)
item.state = newValue1
item = openhab.get_item(‘OH_Voltage_2’)
item.state = newValue2

You need to initialise etc as I said before but the example shows you that as well.

Note the python module link I sent is for the OH 2.x version. Their docs tell you to use the OH 1.x branch if required but as Rich says above you really should be upgrading to OH 2.x.

Can I control somehow how often data are pushed to OH ?
I see one problem here … in my case, so I must share one modbus serial with two processes: OH binding and python, so when I launch python from OH I can know when I can do this (just after a binding). In this script I think I can not control it and I will have errors when two processes want to use modbus in same time.
If I understand this addon will update items externally from python side ?

OH1 -> OH2, I was thinking about it, but last time when I consider it most of bindings (like modbus, 1wire, exec, milight, etc) was in beta state and I was afraid off … maybe it is good time now :slight_smile:

And this is what I was trying to explain above. As long as openHAB is running the OH mobus binding will be running. And as long as the mobus binding is running it will have a lock on the serial device which will prevent your Python scripts from accessing it, even if the binding isn’t actively using it.

Note, if you follow the Migration Tutorial, you will initially set up your current config, including using the exact same 1.x verison bindings as you are using now first. Then you can, one at a time and when ever you want to, migrate those 1.x bindings that have a 2.x verison to the 2.x version.

To provide some clarity of what Ray is suggesting.

If you install the JSR223 addon and required languages you will be able to write Rules in Jython. So instead of having a Rules DSL Rule that calls a Python script, your Rules will themselves be written in Python and you can implement your mobus stuff there (note the caveat above again, this won’t work with the the mobus binding already installed and running).

You can choose to execute the script as and when you like. But as Rich says you need sort out the lock on the serial device.

That would be taking it to the next level and probably worth it in the long run but short term you could still just executeCommandLine from a normal DSL rule when you want it.

For testing could you try going back to basics on a clean OH2 install (presume you can backup and restore your existing install) and see if you can get the modbus binding working as you want it? If that works then migrate 1 -> 2 and retest. May help you move to OH2…

Guys thank you for your help !

I will try this addon for python/openhab and probably will launch python from OH rule - this will let me control a timing.

For Modbus serial I have done some tests, so it looks that binding lock serial only during refresh/modbus connection, after that I can run python script which use serial without collision.

I have one more question. I tried (ver 0.1 since I have OH1) and it looks that my script is quite slow (few seconds to execute few lines of codes). I think it is due to following instruction:

#fetch all items
items = openhab.fetch_all_items(base_url)

Does it mean that python map all my items to some virtual variables ?
If so it is non sense to execute it every 20-30 sec (this is what I plan to read modbus).
Can I just declare that python will read part of items and then use few of them:

item = openhab.get_item(‘OH_Voltage_1’)
item.state = newValue1
item = openhab.get_item(‘OH_Voltage_2’)
item.state = newValue2

That is there in the python module examples just to show you can. Don’t do it unless you need it which I don’t think you do :slight_smile:

I have:

import openhab
base_url = 'http://localhost:8080/rest
myItem = items.get(‘Voltage_L1’)
myItem.state = 380

Then I got:

File “VL1.py”, line 58, in
myItem = items.get(‘Voltage_L1’)
NameError: name ‘items’ is not defined

If I add:

items = openhab.fetch_all_items(base_url)

There is no error, since as I far as I undestand this it definition of “items”.
Question how to define items without fetch all items ?

EDIT:
Ok I found that I can use:

myItem = openhab.get_item(base_url, ‘Voltage_L1’)

then I do not need to fetch all :slight_smile:

In my example I posted:

item = openhab.get_item(‘OH_Voltage_1’)

Subtle difference but you need you ask the ‘openhab’ item for the item you want. You’re asking the ‘items’ array for the item hence why you would then need to fetch all in to ‘items’.