I’m attempting to write a module that I can use across multiple Rules to manage antiflapping Timers. If you saw my other thread on this topic, I’ve decided to go down the Group membership path. I’ve encountered two problems, one of which I’ve spend some time trying to debug, the other of which I’ve not.
Problem 1: I have a test Rule that triggers on System started. It calls my module and tries to set a 1 second antiflapping Timer using my module. So far so good.
My module is a class that creates an AntiflappingTimers Group if one doesn’t exist. This detail is important because it means that the scope is successfully imported and usable at this point.
When a Rule calls the set
method on my class, a new Timer gets created and I store it along with a bunch of other information in a dict. Again, so far so good.
The Timer calls a function that is also part of this module class and I do successfully have the function successfully called.
But when I try to use scope.ir.getItem[itemName]
I get
2019-07-28 18:41:11.656 [ERROR] [sr223.jython.rules.Antiflapping test] - Traceback (most recent call last):
File "/openhab/conf/automation/lib/python/core/log.py", line 51, in wrapper
return fn(*args, **kwargs)
File "/openhab/conf/automation/lib/python/personal/antiflapping_timer.py", line 29, in not_flapping
scope.ir.getItem("AntiflappingTimers").removeMember(scope.ir.getItem(itemName))
File "/openhab/conf/automation/lib/python/core/jsr223.py", line 42, in __getattr__
scope = get_scope()
File "/openhab/conf/automation/lib/python/core/jsr223.py", line 28, in get_scope
raise EnvironmentError("No JSR223 scope is available")
EnvironmentError: No JSR223 scope is available
I’ve tried importing the scope and ir directly in the function. I’ve tried passing the scope to the function. I’ve tried importing the scope at the top of the file. I continue to get this error. The code is as follows.
"""
A class to centralize antiflapping timer logic for multiple Items
"""
from core.jsr223 import scope
from core.items import add_item
from core.rules import rule
from core.triggers import when
from core.actions import ScriptExecution
from org.joda.time import DateTime
from core.log import log_traceback
class Antiflapping_Timers(object):
"""Creates an openHAB Timer and calls the passed in method. It manages
keeping track of the timers and cleaning up after they expire
"""
def __init__(self, log):
self.timers = {}
self.log = log
if scope.ir.getItems("AntiflappingTimers") == []:
log.info("Creating AntiflappingTimers Group")
add_item("AntiflappingTimers", item_type="Group", label="AntiflappingTimers")
@log_traceback
def not_flapping(self, scope, itemName):
# from core.jsr223.scope import ir
self.log.info("{} is not flapping!".format(itemName))
scope.ir.getItem("AntiflappingTimers").removeMember(scope.ir.getItem(itemName))
# self.log.info("Removed {} from group".format(itemName))
# self.timers[itemName][not_flapping]()
# self.log.info("Called function")
@log_traceback
def set(self, itemName, interval, not_function, flapping_function):
"""
If a Timer alreday exists, cancel it and delete it and create a new one:
Args
itemName: The name of the Item the Timer is watching for flapping
interval: how long the item need to remain in the same state to be considered not flapping in milliseconds
not_function: function to call when the Item is determined to not be flapping
flapping_function: function to call when the Item is determined to be flapping
"""
if itemName in self.timers:
self.log.warning("There is already an antiflapping timer for {}".format(itemName))
self.timers[itemName].cancel()
del self.timers[itemName]
self.log.info("Creating the antiflapping timers for {}".format(itemName))
item = scope.ir.getItem(itemName)
scope.ir.getItem("AntiflappingTimers").addMember(item)
self.timers[itemName] = {"orig_state": item.state,
"flapping": flapping_function,
"not_flapping": not_function,
"timer": ScriptExecution.createTimer(DateTime.now().plusMillis(interval), lambda: self.not_flapping(scope, itemName))}
# @rule("Listen for flapping", description="A Rule to listen for flapping of Items")
# @when("Member of AntiflappingTimers changed")
# def flapping(self, event):
# self.log("{} is flapping!".format(event.itemName))
# self.timers[itemName][flapping]()
# scope.getItem("AntiflappingTimers").removeMember(event.itemName)
Problem 2 is that creation of the Rule inside the class. Is that not allowed? As I’ve said, I’ve not really looked into this one yet and I suspect the OpenWeatherMap module in the community modules will be helpful.
Thanks