I have come up with a solution (see code below), that works only partially, after I figured out the issue with the proxy switch and the sitemap (see previous comment).
Now I have the issue, that my rule sometimes executes the code correctly and sometime not. However, I always get the output of:
self.log.info("Turning projector screen ON");
or
self.log.info("Turning projector screen OFF");
I suspect this issue might have to do with the class StatePersistingInterlockedOnOffController
, which keeps internal state using the files .lock
and .current_state
. For instance I cannot even find, where .lock
is created.
I would be very grateful, if you (and whoever else wants to) could look over it and suggest improvements. I suspect there will be many, e.g.:
- Where to put the
codesend
binary?
- Where to put the files
.lock
and .current_state
? (if I should continue using them)
- Naming conventions.
- etc.
I suspect the internal state-keeping is not necessary, now that I use the Jython engine?! I wrote it this way, because I thought I would use the Exec
binding. But doing away with them would probably require using persistence, which I have not yet touched.
Any suggestions are much appreciated!
Here is my current code for the rule, which I put in /etc/openhab2/automation/jsr223/python/personal/project_screen_controller_rule.py
:
from core.rules import rule
from core.triggers import ItemStateUpdateTrigger
import sys
import os.path
from time import sleep
import os
class StatePersistingInterlockedOnOffController(object):
def __init__(self, on_command, off_command, interlock_timeout):
self.on_command = on_command
self.off_command = off_command
self.interlock_timeout = interlock_timeout # lock timeout in [s]; after this time the execution is aborted
self.lock_file_path = "/etc/openhab2/automation/jsr223/python/personal/.lock"
self.state_file_path = "/etc/openhab2/automation/jsr223/python/personal/.current_state"
self.lock_wait_time = 0.5 # wait time in [s] between checking lock
def turn_on(self):
self.execute("ON", lambda: (self.on_command()))
def turn_off(self):
self.execute("OFF", lambda: (self.off_command()))
def execute(self, target_state, command):
self.wait_for_lock_file_release()
self.create_lock_file()
if self.already_in_state(target_state):
print("Already in target state: " + target_state)
else:
command()
self.update_state(target_state)
self.delete_lock_file()
def already_in_state(self, target_state):
if not os.path.isfile(self.state_file_path):
# no state_file_path file exists: create it with desired target_state, return False to move into that state
self.update_state(target_state)
return False
state_file = open(self.state_file_path, "r+")
current_state = state_file.read()
if current_state == target_state:
return True
def update_state(self, state):
state_file = open(self.state_file_path, "w")
state_file.write(state)
def wait_for_lock_file_release(self):
"""
Checks if the script is already running in another instance by checking if the .lock file exists.
If so, waits until it is removed or timeout is hit.
"""
if not os.path.isfile(self.lock_file_path):
return # no lock file exists, so return directly
print("Waiting for previous execution to finish.")
total_wait_time = 0
while (os.path.isfile(self.lock_file_path)):
sleep(self.lock_wait_time)
total_wait_time += self.lock_wait_time
if total_wait_time > self.interlock_timeout:
raise Exception(
"Timeout hit, while waiting for previous run to finish. Please check, if the lock file was not removed during previous execution.")
def create_lock_file(self):
try:
lock_file = open(self.lock_file_path, "w+")
lock_file.close()
except IOError:
print("Error: Could not create lock-file.")
# EXIT AND RETURN ERROR
def delete_lock_file(self):
try:
os.remove(self.lock_file_path)
except IOError:
print("Could not delete lock file.")
# EXIT AND RETURN ERROR
# controller setup: define commands and interlock timeout and initialize controller
def down():
os.system("/etc/openhab2/automation/jsr223/python/personal/codesend 14486692")
def up():
os.system("/etc/openhab2/automation/jsr223/python/personal/codesend 14486690")
def stop():
os.system("/etc/openhab2/automation/jsr223/python/personal/codesend 14486696")
def on_command():
down(), sleep(15.65), stop()
def off_command():
up(), sleep(17), stop()
@rule("Projector screen trigger rule", description="This rule triggers the projector screen controller, when its switch is activated.", tags=["Projector screen rule"])
class ExampleExtensionRule(object):
def __init__(self):
self.triggers = [ItemStateUpdateTrigger("projector_screen").trigger]
self.first_switch = True
lock_timeout = 20
self.controller = StatePersistingInterlockedOnOffController(on_command, off_command, lock_timeout)
def execute(self, module, inputs):
item_state = str(inputs['state'])
if(item_state in "ON"):
self.log.info("Turning projector screen ON");
self.controller.turn_on()
if(item_state in "OFF"):
self.log.info("Turning projector screen OFF");
self.controller.turn_off()