Idea: use python to directly generate rules files

Habapp is a wholly separate service written in Python3. It’s kind of like Node Red in that respect. It interacts with OH though it’s REST API. Consequently, it works with any version of OH.

2 Likes

The framework and tools for you to do this have been provided… why use the old technology? The vision of a robot that works furiously with a printing press comes to mind :smiley:.

Using a Python script to generate a DSL rule sounds like a lack of awareness of the new rule engine, scripted automation, and the helper libraries. With these, the original Java code could have been used, rather than rewriting it in Python. But if you prefer Python, it could all be done within the new rule engine.

Rich, were you referring to GraalVM. This does not allow for the use of “pure python” though, but an implementation of Python written specifically for GraalVM, similar to Jython. I recently made a comment about this here. IMO, it will be a long while before it would be worth the effort to integrate GraalVM into OH for automation. However, I do plan to build bundles for other languages.

Thanks to all,

This thread gave a lot of new insights. Did quite some reading the last days.
To summarize my use cases it is something like this:

  1. Download a website once a day.
  2. Process the site to extract some information
  3. Update OH2 items with the new information
  4. Make sure timers are triggered anywhere between “today” and “in 3 months”
  5. (make sure item 4. still works after each reload/restart of OH2.)

So, what I learnt:

HABapp
Much similarities between the “ptyhon-openhab” package I used. But can do a lot more, for example being triggered by OH2 events. All python packages can be added. So using PyQuery is possible (for step 2 in my use case). But also some drawbacks as it is a “side” project. Not maintained by the OH community if I understand right. Still under development.

–> might test this solution for the next idea that comes up and requires some python (according my vision)

new rule engine
Looked at it, but as I have no programming background, I have no clue how to get items 1 & 2 done with it.

–> no go. Have no clue where to start and no idea if my project is possible at all.

scripted automation & helper libraries
Probably should invest some time in this solution. But will be another steep learning curve I guess.

–> depending on the time I have will invest time to learn about it.

But as always in life, you cannot do everything the best possible way. The solution I made in the 1st post worked and was done within 4 hours from scratch. So the trade-off between spending time on domotica and construction work on the house, was acceptable (according my wife :joy: )

As a Mechatronics engineer I already discovered there are way to many programming languages & software tools important these days and there is only time to learn a few up to a level I can use it as part of making a living. So on the programming field, for now, I limited myself to:
C is for microprocessor (some C++ for arduino/ esp8266)
Python for scripting & dataprocessing (in case Matlab is not available)
C# for windows UI programs.

(not to mention the software tools I need to learn for mechanical design & electrical design).

OH2 is working fine now as I was able to start over in our new house, but it has been a steep learning curve so far. My 2 best friends both run Domoticz and they seem to have a lot of stuff up and running fast (I did not say faster). I always need a lot of googling for examples to get things correctly running --> so a general remark: keep in mind that complete working examples, and not code snippets or API interface definitions are, the most useful source of information for users like me.

Basically this forum is the best documentation there is.

Grtz Matthijs

Pretty much the same way as you would in Rules DSL or in native Python. Call the sendHttpGetRequest Action to download the page and depending on the format of the downloaded contents use the openHAB transformation action, either JSONPATH, XPATH, or REGEX.

There are tons of examples on the forum and we can help if you run into trouble. For the most part you don’t need to know how to program this sort of thing, you can just use the tools openHAB provides.

One of the goals of Rules (any version of rules) and openHAB is that you don’t need to know the details about how to connect to devices or web pages or specific technologies.

:+1: for your quick reply.

But in one of the above post following link was given for the new rule engine:
new rules engine

It pretty well illustrates the steep learning curve issue of OH. On that page, there is no information at all which gives me the impression that my “use case” can be done. So, my conclusion was / is : no go.

So, I will search the forum and come back if needed :slight_smile:

Grtz Matthijs

You can’t put every use case in every page of documentation. See How to get started (there is no step-by-step tutorial)

So having a familiarity of what OH can do is important. That’s why we emphasize reading the docs so much. That page doesn’t say anything about getting web pages but it does take about openHAB Actions. And there is a page in the docs that goes over all the built in actions including the sendHttpGetRequest.

To be successful with any home automation system you have to be able to piece together tutorials and reference guides that address on small party of what you are trying to achieve because there never will be an end to end tutorial for your specific system. Your system is unique.

I am the maintainer and since I literally answered your post I think I’m part of the OH community, too. :wink:

This!

All,

Point(s) taken. I will read more before complaining :flushed:

But, please consider my complaints also a valuable feedback on the OH learning curve. As a skilled engineer I find myself lost every now and then within the OH way of working. Maybe it is just as it is, maybe you see opportunities. :slight_smile:

Cheers,
Matthijs

While relaxing tonight, I whipped up an example of using scripted automation with Jython and the helper libraries to scrape the webpage to update DateTimeItems and send notifications through Telegram (the OH 1.x action version). The easiest way to install Jython is to use the beta bundle. Hopefully this is useful for you!

"""
This script contains a rule that will scrape https://afvalkalender.waalre.nl
for collection times twice a day and update Items with the dates. The script
will create the Items needed, if they do not exist.
"""
import re

from core.rules import rule
from core.triggers import when
from core.items import add_item
from core.actions import HTTP, Telegram

URI = "https://afvalkalender.waalre.nl/adres/5581BG:17"

def monthToNum(shortMonth):
    return{
        'jan' : 1,
        'feb' : 2,
        'mrt' : 3,
        'apr' : 4,
        'mei' : 5,
        'jun' : 6,
        'jul' : 7,
        'aug' : 8,
        'sep' : 9,
        'okt' : 10,
        'nov' : 11,
        'dec' : 12
    }[shortMonth]

def convert_date(day_text):
    day_list = day_text.split(" ")
    new_date = DateTimeType().zonedDateTime.withMonth(monthToNum(day_list[2])).withDayOfMonth(int(day_list[1])).withHour(0).withMinute(0).withSecond(0).withNano(0)
    return new_date

if ir.getItems("Afval_gft") == []:
    add_item("Afval_gft", item_type="DateTime", label="Groente [%s]", category="Calendar", groups=[], tags=[])
if ir.getItems("Afval_pmd") == []:
    add_item("Afval_pmd", item_type="DateTime", label="Plastic [%s]", category="Calendar", groups=[], tags=[])
if ir.getItems("Afval_papier") == []:
    add_item("Afval_papier", item_type="DateTime", label="Papier [%s]", category="Calendar", groups=[], tags=[])
if ir.getItems("Afval_kga") == []:
    add_item("Afval_kga", item_type="DateTime", label="Chemisch [%s]", category="Calendar", groups=[], tags=[])
if ir.getItems("Afval_rest") == []:
    add_item("Afval_rest", item_type="DateTime", label="Restafval [%s]", category="Calendar", groups=[], tags=[])

@rule("Send trash pickup notifications")
@when("Time cron 0 30 7 * * ?")
@when("Time cron 0 30 19 * * ?")
def send_trash_pickup_notifications(event):
    # get the page
    the_page = HTTP.sendHttpGetRequest(URI, 5000)
    if the_page:
        contents = the_page.decode('utf-8')

        # Find the part of the page with id=ophaaldata
        START = '<ul id="ophaaldata" class="line">'
        END = '</ul>'
        section = re.search("{}(.*?){}".format(START, END), contents, re.DOTALL).group(1)

        # Split the section based on "\n" into lines.
        section_list = section.split("\n")

        for index, line in enumerate(section_list):
            dag_text = re.search('<i class="date">(.*?)</i>', line, re.DOTALL)
            if dag_text:
                dag_text = dag_text.group(1)
                pickup_date = convert_date(dag_text)
                pickup_type = None
                soort_afval = re.search('<i>(.*?)</i>', section_list[index + 1], re.DOTALL).group(1)

                if "Groente" in soort_afval:
                    pickup_type = "Groente"
                    item_name = "Afval_gft"
                elif "Plastic" in soort_afval:
                    pickup_type = "Plastic"
                    item_name = "Afval_pmd"
                elif "Papier" in soort_afval:
                    pickup_type = "Papier"
                    item_name = "Afval_papier"
                elif "Chemisch" in soort_afval:
                    pickup_type = "Chemisch"
                    item_name = "Afval_kga"
                elif "Restafval" in soort_afval:
                    pickup_type = "Restafval"
                    item_name = "Afval_rest"

                send_trash_pickup_notifications.log.debug("{}: {}".format(pickup_type, pickup_date))
                events.sendCommand(item_name, pickup_date.toString())
                if pickup_type and DateTimeType().zonedDateTime.toLocalDate() == pickup_date.toLocalDate():
                    Telegram.sendTelegram("domoticahajerstijn", "Afval notificatie: {}".format(pickup_type))
    else:
        send_trash_pickup_notifications.log.warn("Communication failure")
2 Likes

:grinning: Truely a great example. That is super!
For sure a number of lines which would have taken me a long time to figure out.

Going to read and install the needed preconditions to test the script. Valuable reference design.

Thanks!

1 Like

Think I need help.

Tried to follow the installation procedure of 5jver as exact as possible (although confusing to do 2 procedures combined), I must have done something wrong.

The javascript part is working. The “hello_world.js” works correctly and gives a message very 1 seconds (removed it again to de-clutter the logging)

The for python version I get:

2020-01-03 18:33:11.845 [INFO ] [me.core.service.AbstractWatchService] - ScriptEngine for py not available
2020-01-03 18:33:12.850 [INFO ] [me.core.service.AbstractWatchService] - ScriptEngine for py not available

2020-01-03 18:26:26.886 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'velbus.rules'
2020-01-03 18:26:29.051 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'aqara.rules'
2020-01-03 18:26:29.899 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'sonsos.rules'
2020-01-03 18:26:30.064 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'telegram.rules'
2020-01-03 18:26:30.339 [INFO ] [thome.model.lsp.internal.ModelServer] - Started Language Server Protocol (LSP) service on port 5007
2020-01-03 18:26:33.215 [INFO ] [.dashboard.internal.DashboardService] - Started Dashboard at http://192.168.3.130:8080
2020-01-03 18:26:33.220 [INFO ] [.dashboard.internal.DashboardService] - Started Dashboard at https://192.168.3.130:8443
2020-01-03 18:26:39.964 [INFO ] [ternal.dhcp.DHCPPacketListenerServer] - DHCP request packet listener online
2020-01-03 18:26:40.421 [INFO ] [b.core.service.AbstractActiveService] - NetworkHealth Refresh Service has been started
2020-01-03 18:26:41.451 [DEBUG] [.AutomationResourceBundlesEventQueue] - Process bundle event 2, for automation bundle 'org.openhab.core.automation' 
2020-01-03 18:26:41.465 [DEBUG] [vider.AbstractResourceBundleProvider] - Parse rules from bundle 'org.openhab.core.automation' 
2020-01-03 18:26:41.465 [DEBUG] [.AutomationResourceBundlesEventQueue] - Process bundle event 32, for automation bundle 'org.openhab.core.automation.module.script.rulesupport' 
2020-01-03 18:26:41.469 [DEBUG] [vider.AbstractResourceBundleProvider] - Parse rules from bundle 'org.openhab.core.automation.module.script.rulesupport' 
2020-01-03 18:26:41.958 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added CoreModuleHandlerFactory
2020-01-03 18:26:41.962 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added EphemerisModuleHandlerFactory
2020-01-03 18:26:41.965 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added TimerModuleHandlerFactory
2020-01-03 18:26:41.968 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added AnnotatedActionModuleTypeProvider
2020-01-03 18:26:42.043 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added AnnotatedThingActionModuleTypeProvider
2020-01-03 18:26:42.124 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added MediaModuleHandlerFactory
2020-01-03 18:26:42.253 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added GenericScriptEngineFactory
2020-01-03 18:26:42.262 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - ScriptEngineFactory details for Oracle Nashorn (1.8.0_152): supports ECMAScript (ECMA - 262 Edition 5.1) with file extensions [js], names [nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript], and mimetypes [application/javascript, application/ecmascript, text/javascript, text/ecmascript]
2020-01-03 18:26:42.266 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added NashornScriptEngineFactory
2020-01-03 18:26:42.270 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - ScriptEngineFactory details for Oracle Nashorn (1.8.0_152): supports ECMAScript (ECMA - 262 Edition 5.1) with file extensions [js], names [nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript], and mimetypes [application/javascript, application/ecmascript, text/javascript, text/ecmascript]
2020-01-03 18:26:42.276 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Removed GenericScriptEngineFactory
2020-01-03 18:26:42.280 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Removed NashornScriptEngineFactory
2020-01-03 18:26:42.334 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added GenericScriptEngineFactory
2020-01-03 18:26:42.338 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - ScriptEngineFactory details for Oracle Nashorn (1.8.0_152): supports ECMAScript (ECMA - 262 Edition 5.1) with file extensions [js], names [nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript], and mimetypes [application/javascript, application/ecmascript, text/javascript, text/ecmascript]
2020-01-03 18:26:42.341 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added NashornScriptEngineFactory
2020-01-03 18:26:42.345 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - ScriptEngineFactory details for Oracle Nashorn (1.8.0_152): supports ECMAScript (ECMA - 262 Edition 5.1) with file extensions [js], names [nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript], and mimetypes [application/javascript, application/ecmascript, text/javascript, text/ecmascript]
2020-01-03 18:26:42.350 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added ScriptModuleHandlerFactory
2020-01-03 18:26:42.445 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added ScriptedCustomModuleHandlerFactory
2020-01-03 18:26:42.467 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added ScriptedPrivateModuleHandlerFactory
2020-01-03 18:26:51.400 [INFO ] [.transport.mqtt.MqttBrokerConnection] - Starting MQTT broker connection to '192.168.3.130' with clientid 6a423c79-94ee-48b8-8f5b-d67d43d2837f
2020-01-03 18:26:51.521 [INFO ] [.transport.mqtt.MqttBrokerConnection] - Starting MQTT broker connection to '192.168.3.130' with clientid 07e7727b-1a7e-4a76-bb93-49983cb7f54d
2020-01-03 18:26:52.324 [INFO ] [thome.model.script.VelBus init rules] - finished
2020-01-03 18:26:52.335 [INFO ] [me.model.script.Aqara Initiate.rules] - finished
2020-01-03 18:26:53.062 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'velbus.rules'
2020-01-03 18:26:54.486 [INFO ] [openhab.ui.paper.internal.PaperUIApp] - Started Paper UI at /paperui
2020-01-03 18:27:04.720 [INFO ] [thome.model.script.VelBus init rules] - finished
2020-01-03 18:27:07.530 [INFO ] [me.core.service.AbstractWatchService] - Loading script 'javascript/core/000_startup_delay.js'
2020-01-03 18:27:10.168 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'js' with identifier: file:/etc/openhab2/automation/jsr223/javascript/core/000_startup_delay.js
2020-01-03 18:27:10.379 [INFO ] [jsr223.javascript.core.startup_delay] - Checking for initialized context
2020-01-03 18:27:10.459 [INFO ] [jsr223.javascript.core.startup_delay] - Context initialized... waiting 30s before allowing scripts to load
2020-01-03 18:27:40.517 [INFO ] [jsr223.javascript.core.startup_delay] - Complete

What went wrong?

Grtz Matthijs

Which version of OH are you running and which jar file from the post Scott linked to did you install? What’s the permissions on that jar file after you copied it into your addons folder.

OH 2.5.0-M5-1

JAR:

/usr/share/openhab2/addons#
-rwxr-xr-x 1 openhab openhab   112191 Aug 15 08:22 org.openhab.binding.velbus-2.5.0-SNAPSHOT13d.jar
-rwxr-xr-x 1 openhab openhab 37403000 Jan  3 17:19 org.openhab.core.automation.module.script.scriptenginefactory.jython-2.5.0-SNAPSHOT.jar

Hmmmm, I’m not certain that the jar will work on a version of OH that old. You may need to move to 2.5 M6 or preferably 2.5.0 release.

oke. Last time I tried to update, I got the “famous” mqtt error (and while figuring that out, killed my installation). Will make a backup tonight and update.

5iver indeed states: Have OH 2.5 (S1778) or newer

So lets try.

Yes, you will need 2.5 in order to get a last minute PR for openHAB core that let’s this bundle work.

Progres!

Almost there. Upgrade to 2.5.0 worked. Jython is running and the script is loaded.

BUT, there is a small bug in the script . See below. So now my question is, how can I run the script without setting the crontab every time. What is the “commandline way” to run these scripts?

# File "/etc/openhab2/automation/lib/python/core/log.py", line 51, in wrapper
# return fn(*args, **kwargs)
# File "<script>", line 91, in send_trash_pickup_notifications
# AttributeError: 'java.time.ZonedDateTime' object has no attribute 'zonedDateTime'

So something is wrong with this part of the code and I like to debug / test various alternatives I found online.

def convert_date(day_text):
    day_list = day_text.split(" ")
    new_date = DateTimeType().zonedDateTime.withMonth(monthToNum(day_list[2])).withDayOfMonth(int(day_list[1])).withHour(0).withMinute(0).withSecond(0).withNano(0)
    return new_date

How to proceed?
(of course the bug fix is appreciated too, but the general question remains: how to run scripts on demand).

Thanks,
Matthijs

It’s quite easy to test scripts. Here are 3 options…

  1. Add a “System started” trigger, which will trigger the rule when the file is saved. This is how I tested this rule before posting.
@when("System started")
  1. Rather than trigger the rule, just call the rule function in the script. This can be tricky, depending on the event object that is expected. Fortunately, the event object for cron triggers and startup triggers are None.
send_trash_pickup_notifications(None)
  1. Just create another script and add the body of the rule function, or portions of it. I have several test scripts with commented out tests that I am constantly using when working on things.

To debug this, you will probably want to wrap things in try/except. This is done for you with the rule decorator, along with a traceback, using the core.log.log_traceback decorator, which comes in handy during testing.

As for the error, I’ve made a small fix in the 4th line from the end that I had commented out during testing because I don’t use Telegram. convert_date returns a ZonedDateTime, so pickup_date is a ZonedDateTime, not a DateTimeType.

In addition to the three options Scott provided I have a couple more.

  1. Click the play button next to the Rule in PaperUI. As with running the function from another script or System started, it may not work if you are expecting something specific in the event object. I suspect this can be done from the REST API docs and the Karaf console too.

  2. Manually command or update the triggering item using another rule, a script, the REST API docs, or the Karaf console. This approach will be the closest to how the rule will actually run in real life for Item or Member of triggered rules.

Guess what? Python is a scripting language used in programming.

Speaking from experience, it takes time and dedication to learn Python. I took the time myself to learn it in the past 18 months.

If you cannot follow simple programming. you could not implement your idea for this thread anyway.