One of the benefits of Jython over the openHAB Xtext scripts is that you can use the full power of Python packages and modules to structure your code into reusable components.
When using the instructions in the Quick Start Guide, these modules should be located in /automation/lib/python/
Changes to a module will not take effect until all other modules and scripts that have imported it have been reloaded. Restarting OH will remedy this, but another option is to use the reload() function:
import myModule
reload(myModule)
import myModule
If using imports like from core.triggers import when
, or if the module is in a package, you will need to use:
import sys
from core.triggers import when
reload(sys.modules['core.triggers'])
from core.triggers import when
... or import the whole module to reload it...
from core.triggers import when
import core.triggers
reload(core.triggers)
from core.triggers import when
Custom modules and packages can also be added into /automation/lib/python/personal/
. Modules do not have the same scope as scripts, but this can be remedied by importing scope
from the jsr223
module. This will allow for things like accessing the itemRegistry:
from core.jsr223 import scope
scope.itemRegistry.getItem("MyItem")
Module: core.triggers
- ItemStateChangeTrigger
- ItemStateUpdateTrigger
- ItemCommandStrigger
- ItemEventTrigger (based on "core.GenericEventTrigger")
- CronTrigger
- StartupTrigger - fires when rule is activated (implemented in Jython)
- DirectoryEventTrigger - fires when directory contents change (Jython, see related component for more info).
- ItemAddedTrigger - fires when rule is added to the RuleRegistry (implemented in Jython)
- ItemRemovedTrigger - fires when rule is removed from the RuleRegistry (implemented in Jython)
- ItemUpdatedTrigger - fires when rule is updated in the RuleRegistry (implemented in Jython, not a state update!)
- ChannelEventTrigger - fires when a Channel gets an event e.g. from the Astro Binding
- @when("Item Test_Switch_1 received command OFF")
- @when("Item Test_Switch_2 received update ON")
- @when("Member of gMotion_Sensors changed to OFF")
- @when("Descendent of gContact_Sensors changed to ON")
- @when("Thing kodi:kodi:familyroom changed")# Thing statuses cannot currently be used in triggers
- @when("Channel astro:sun:local:eclipse#event triggered START")
- @when("System started")#
System started
and 'System shuts down' cannot currently be used as a trigger - @when("55 55 5 * * ?")
This module includes trigger subclasses and function decorators to simplify Jython rule definitions.
Trigger classes for wrapping Automation API (see Using Jython extensions):
Trigger function decorator (see the core.rules
module for the rule decorator that is used in conjunction with the trigger decorator:
Module: core.rules
The rules module contains some utility functions and a decorator that can 1) convert a Jython class into a SimpleRule
, 2) decorate the trigger decorator (@when) to create a SimpleRule
.
The following example shows how the rule decorator is used to decorate a class:
from core.rules import rule
from core.triggers import StartupTrigger
@rule("My example rule")
class ExampleRule(object):
"""This doc comment will become the ESH Rule documentation value for Paper UI"""
#def __init__(self):
# self.triggers = [ StartupTrigger().trigger ]
def getEventTriggers(self):
return [ StartupTrigger().trigger ]
def execute(self, module, inputs):
self.log.info("rule executed")
The rule
decorator adds the SimpleRule base class and will call getEventTriggers
to get the triggers, or you can define a constructor and set self.triggers
to your list of triggers (commented out in the example).
The addRule
function is similar to the automationManager.addRule
function except that it can be safely used in Jython modules (versus scripts).
Since the automationManager
is different for every script scope, the core.rules.addRule
function looks up the automation manager for each call.
The decorator also adds a log object based on the name of the rule (self.log
can be overridden in a constructor) and
wraps the event trigger and execute
functions in a wrapper that will print nicer stack trace information if an exception
is thrown.
The following example shows how the rule decorator is used to decorate the trigger decorator:
from core.rules import rule
from core.triggers import when
@rule("My example rule")
@when("Time cron 0/10 * * * * ?")
def exampleDecoratedCronRule(event):
# do stuff
Module: core.actions
This module discovers action services registered from OH1 or OH2 bundles or add-ons.
The specific actions that are available will depend on which add-ons are installed.
Each action class is exposed as an attribute of the core.actions
Jython module.
The action methods are static methods on those classes (don't try to create instances of the action classes).
from core.actions import Astro
from core.log import logging, LOG_PREFIX
from java.util import Date
log = logging.getLogger(LOG_PREFIX + ".astro_test")
# Use the Astro action class to get the sunset start time.
log.info("Sunrise: {}".format(Astro.getAstroSunsetStart(Date(2017, 7, 25), 38.897096, -77.036545).time))
Module: core.log
This module bridges the Python standard logging
module with ESH logging.
The configuration
module also provides a LOG_PREFIX
variable, which is used throughout the core modules and scripts including the log
module.
LOG_PREFIX can be modified based on personal preference, which will change the default logger.
Example usage:
from core.log import logging, LOG_PREFIX
log = logging.getLogger(LOG_PREFIX + ".test_logging_script")
logging.critical("Logging example from root logger [TRACE]")
logging.debug("Logging example from root logger [DEBUG]")
logging.info("Logging example from root logger [INFO]")
logging.warning("Logging example from root logger [WARN]")
logging.error("Logging example from root logger [ERROR]")
logging.getLogger("test_logging_script").info("Logging example from logger")
log.info("Logging example from logger, using text appended to LOG_PREFIX")
Module: core.items
This module allows runtime creation and removal of items.
import core.items
core.items.add("_Test", "String")
# later...
core.items.remove("_Test")
Module: core.testing
One of the challenges of ESH/openHAB rule development is verifying that rules are behaving correctly and haven't broken as the code evolves. This module supports running automated tests within a runtime context. To run tests directly from scripts:
import unittest # standard Python library
from core.testing import run_test
class MyTest(unittest.TestCase):
def test_something(self):
"Some test code..."
run_test(MyTest)
The module also defines a rule class, TestRunner
that will run a testcase
when an switch item is turned on and store the test results in a string item.
Module: core.osgi
Provides utility function for retrieving, registering and removing OSGI services.
import core.osgi
item_registry = osgi.get_service("org.eclipse.smarthome.core.items.ItemRegistry")
Module: core.osgi.events
Provides an OSGI EventAdmin event monitor and rule trigger. This can trigger off any OSGI event (including ESH events). Rule manager events are filtered to avoid circular loops in the rule execution.
class ExampleRule(SimpleRule):
def __init__(self):
self.triggers = [ core.osgi.events.OsgiEventTrigger() ]
def execute(self, module, inputs):
event = inputs['event']
# do something with event
Module: core.jsr223
One of the challenges of JSR223 scripting with Jython is that Jython modules imported into scripts do not have direct access to the JSR223 scope types and objects. This module allows imported modules to access that data. Example usage:
# In Jython module, not script...
from core.jsr223.scope import events
def update_data(data):
events.postUpdate("TestString1", str(data))
Module: core
This module (really a Python package) patches the default scope items
object
so that items can be accessed as if they were attributes (rather than a dictionary).
It can also be used as a module for registering global variables that will outlive script reloads.
import core
print items.TestString1
Note that this patch will be applied when any module in the core
package is loaded.