Ok, I think I reached something that I can use and of course you were correct in using class Rule instead of class Item! Im sure improvements can be made, but Im leaving code here anyway.
A note that I seem to violate one of you given advices when I assign the Rule to a class member (see usage-part). Is there a solution around this? I could not get it to work properly using self.get_rule(filename.py)
Rule — HABApp beta documentation
import logging
from HABApp.openhab.items import StringItem
from HABApp.core.items import Item
from HABApp import Rule
from HABApp.core.events import ValueUpdateEvent, ValueUpdateEventFilter, OrFilterGroup
from datetime import datetime, timedelta
log = logging.getLogger('TestDebug')
class ButtonClick:
# Checks for nbr of consecutive clicks
def __init__(self, delay_ms):
self.last_click = datetime.now()
self.nbr_consecutive_clicks = 0
self.dbl_click_max_delay_ms = delay_ms
self.id_last= ''
def click(self, id_click):
""" checks/returns nbr_consecutive_clicks within timeout """
if id_click != self.id_last:
self.nbr_consecutive_clicks = 1
elif datetime.now() - self.last_click > timedelta(milliseconds=self.dbl_click_max_delay_ms):
self.nbr_consecutive_clicks = 1
else:
self.nbr_consecutive_clicks += 1
self.last_click = datetime.now()
self.id_last = id_click
return self.nbr_consecutive_clicks
class IkeaRemoteE2002(Rule):
__str_click_up = 'on'
__str_click_down = 'off'
__str_click_left = 'arrow_left_click'
__str_click_right = 'arrow_right_click'
__str_hold_up = 'brightness_move_up'
__str_hold_down = 'brightness_move_down'
__str_hold_left = 'arrow_left_hold'
__str_hold_right = 'arrow_right_hold'
__str_hold_release = 'brightness_stop'
def __init__(self, link_item_name, enable_dbl_click=True, click_delay_ms=500):
super().__init__()
if not enable_dbl_click:
click_delay_ms = 1
self.remote_action_string = StringItem.get_item(link_item_name)
self.click_event_filter = OrFilterGroup(
ValueUpdateEventFilter(value=self.__str_click_up),
ValueUpdateEventFilter(value=self.__str_click_down),
ValueUpdateEventFilter(value=self.__str_click_left),
ValueUpdateEventFilter(value=self.__str_click_right)
)
self.remote_action_string.listen_event(callback=self.button_click_update, event_filter=self.click_event_filter)
self.hold_event_filter = OrFilterGroup(
ValueUpdateEventFilter(value=self.__str_hold_up),
ValueUpdateEventFilter(value=self.__str_hold_down),
ValueUpdateEventFilter(value=self.__str_hold_left),
ValueUpdateEventFilter(value=self.__str_hold_right),
ValueUpdateEventFilter(value=self.__str_hold_release)
)
self.remote_action_string.listen_event(callback=self.button_hold_update, event_filter=self.hold_event_filter)
self.countdown_click_timeout = self.run.countdown(expire_time=timedelta(milliseconds=click_delay_ms),
callback=self.button_click_timeout)
self.__button = ButtonClick(delay_ms=click_delay_ms)
self.up_single_click = Item.get_create_item('up_single_click')
self.down_single_click = Item.get_create_item('down_single_click')
self.left_single_click = Item.get_create_item('left_single_click')
self.right_single_click = Item.get_create_item('right_single_click')
self.up_hold = Item.get_create_item('up_hold')
self.down_hold = Item.get_create_item('down_hold')
self.left_hold = Item.get_create_item('left_hold')
self.right_hold = Item.get_create_item('right_hold')
self.hold_release = Item.get_create_item('hold_release')
self.up_double_click = Item.get_create_item('up_double_click')
self.down_double_click = Item.get_create_item('down_double_click')
self.left_double_click = Item.get_create_item('left_double_click')
self.right_double_click = Item.get_create_item('right_double_click')
self.up_multi_click = Item.get_create_item('up_multi_click')
self.down_multi_click = Item.get_create_item('down_multi_click')
self.left_multi_click = Item.get_create_item('left_multi_click')
self.right_multi_click = Item.get_create_item('right_multi_click')
def button_click_update(self, event: ValueUpdateEvent):
self.countdown_click_timeout.stop()
self.__button.click(id_click=event.value)
if self.__button.nbr_consecutive_clicks == 1:
self.countdown_click_timeout.reset()
elif self.__button.nbr_consecutive_clicks == 2:
match event.value:
case self.__str_click_up:
self.up_double_click.post_value(True)
case self.__str_click_down:
self.down_double_click.post_value(True)
case self.__str_click_left:
self.left_double_click.post_value(True)
case self.__str_click_right:
self.right_double_click.post_value(True)
elif self.__button.nbr_consecutive_clicks > 2:
match event.value:
case self.__str_click_up:
self.up_multi_click.post_value(True)
case self.__str_click_down:
self.down_multi_click.post_value(True)
case self.__str_click_left:
self.left_multi_click.post_value(True)
case self.__str_click_right:
self.right_multi_click.post_value(True)
def button_hold_update(self, event: ValueUpdateEvent):
if event.value == self.__str_hold_release:
self.hold_release.post_value(new_value=True)
self.up_hold.post_value_if(equal=True, new_value=False)
self.down_hold.post_value_if(equal=True, new_value=False)
self.left_hold.post_value_if(equal=True, new_value=False)
self.right_hold.post_value_if(equal=True, new_value=False)
else:
self.hold_release.post_value_if(equal=True, new_value=False)
match self.remote_action_string.value:
case self.__str_hold_up:
self.up_hold.post_value(True)
case self.__str_hold_down:
self.down_hold.post_value(True)
case self.__str_hold_left:
self.left_hold.post_value(True)
case self.__str_hold_right:
self.right_hold.post_value(True)
def button_click_timeout(self):
match self.remote_action_string.value:
case self.__str_click_up:
self.up_single_click.post_value(True)
case self.__str_click_down:
self.down_single_click.post_value(True)
case self.__str_click_left:
self.left_single_click.post_value(True)
case self.__str_click_right:
self.right_single_click.post_value(True)
Example of usage:
import logging
from HABApp import Rule
from HABApp.core.events import ValueUpdateEvent, ValueUpdateEventFilter
import typing
# if typing.TYPE_CHECKING: # This is only here to allow
from rules.devices.ikea_remote_E2002 import IkeaRemoteE2002 # type hints for the IDE
log = logging.getLogger('TestDebug')
class TestRemote(Rule):
def __init__(self):
super().__init__()
# I see it can be dangerous to assign class in this way (ie, if the IkeaRemoteE2002 is changed in any way, this Rule is lost?, instead
# self.get_rule('IkeaRemoteE2002') is suggested?
# is it possible to combine .get_rule-method while still keeping the variable-instantiation and its item-instances
# Not encouraged to assign a Rule to a class member see:
# https://habapp.readthedocs.io/en/latest/rule.html#how-to-properly-use-rules-from-other-rule-files
self.my_remote = IkeaRemoteE2002(link_item_name='action_IKEA_ON_OFF_kontor', click_delay_ms=600)
# Assign/create 'click-events'(listen_event) to the item-members (up_single_click, down_double_click etc) of the remote
self.my_remote.up_single_click.listen_event(self.on_up_single_click, ValueUpdateEventFilter())
self.my_remote.up_double_click.listen_event(self.on_up_dbl_click, ValueUpdateEventFilter())
self.my_remote.up_hold.listen_event(self.on_up_hold, ValueUpdateEventFilter(value=True))
self.my_remote.up_multi_click.listen_event(self.on_multi_click, ValueUpdateEventFilter())
self.my_remote.down_single_click.listen_event(self.on_down_single_click, ValueUpdateEventFilter())
self.my_remote.down_double_click.listen_event(self.on_down_dbl_click, ValueUpdateEventFilter())
self.my_remote.down_hold.listen_event(self.on_down_hold, ValueUpdateEventFilter(value=True))
self.my_remote.down_multi_click.listen_event(self.on_down_multi_click, ValueUpdateEventFilter())
self.my_remote.hold_release.listen_event(self.on_hold_release, ValueUpdateEventFilter())
def on_up_single_click(self, event: ValueUpdateEvent):
print(f'{event}')
def on_multi_click(self, event: ValueUpdateEvent):
print(f'{event}')
def on_up_dbl_click(self, event: ValueUpdateEvent):
print(f'{event}')
def on_up_hold(self, event: ValueUpdateEvent):
print(f'{event}')
def on_down_single_click(self, event: ValueUpdateEvent):
print(f'{event}')
def on_down_dbl_click(self, event: ValueUpdateEvent):
print(f'{event}')
def on_down_multi_click(self, event: ValueUpdateEvent):
print(f'{event}')
def on_down_hold(self, event: ValueUpdateEvent):
print(f'{event}')
def on_hold_release(self, event: ValueUpdateEvent):
print(f'{event}')
TestRemote()