Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix button SecondFunc trigger #2367

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 23 additions & 18 deletions components/gpio_control/GPIODevices/simple_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ def callbackFunctionHandler(self, *args):
if inval != GPIO.LOW:
return None

if self.hold_mode in ('Repeat', 'Postpone', 'SecondFunc', 'SecondFuncRepeat'):
return self.longPressHandler(*args)
else:
logger.info('{}: execute callback'.format(self.name))
return self.when_pressed(*args)
return self._handleCallbackFunction(*args)

@property
def when_pressed(self):
Expand All @@ -136,36 +132,45 @@ def when_pressed(self, func):
def set_callbackFunction(self, callbackFunction):
self.when_pressed = callbackFunction

def longPressHandler(self, *args):
logger.info('{}: longPressHandler, mode: {}'.format(self.name, self.hold_mode))
# instant action (except Postpone mode)
if self.hold_mode != "Postpone":
self.when_pressed(*args)
def _handleCallbackFunction(self, *args):
logger.info('{}: handleCallbackFunction, mode: {}'.format(self.name, self.hold_mode))

# action(s) after hold_time
if self.hold_mode == "Repeat":
# Repeated call of main action (multiple times if button is held long enough)
# Instantly call primary action
self.when_pressed(*args)
# Repeated call of primary action if button is held long enough
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
self.when_pressed(*args)

elif self.hold_mode == "Postpone":
# Postponed call of main action (once)
# Postponed call of primary action (once)
if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
self.when_pressed(*args)
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
pass
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
pass

elif self.hold_mode == "SecondFunc":
# Call of secondary action (once)
# execute primary action if not held past hold_time
if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
self.when_held(*args)
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
pass
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
pass
else:
self.when_pressed(*args)

elif self.hold_mode == "SecondFuncRepeat":
# Repeated call of secondary action (multiple times if button is held long enough)
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
# execute primary action if not held past hold_time
if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
self.when_held(*args)
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
self.when_held(*args)
else:
self.when_pressed(*args)

else:
self.when_pressed(*args)

def __del__(self):
logger.debug('remove event detection')
Expand Down
15 changes: 9 additions & 6 deletions components/gpio_control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,18 @@ functionCall: functionCallPlayerPause
However, a button has more parameters than these. In the following comprehensive list you can also find the default values which are used automatically if you leave out these settings:

* **functionCallArgs**: Arguments for primary function, defaults to `None`. Arguments are ignored, if `functionCall` does not take any.

> [!IMPORTANT]
> Since v2.8.0 the behavior of `hold_mode` `SecondFunc` and `SecondFuncRepeat` has changed. The secondary function is no longer triggered additionally to the primary function.
> Now its called exclusively if `hold_time` is reached. The primary function will only be triggered if the button is pressed shorter then `hold_time`!
> Existing configurations may need to adapt to this.
* **hold_mode**: Specifies what shall happen if the button is held pressed for longer than `hold_time`:
* `None` (Default): Nothing special will happen.
* `Repeat`: The configured `functionCall` is repeated after each `hold_time` interval.
* `Repeat`: The configured `functionCall` is instantly called and repeatedly after each `hold_time` interval.
* `Postpone`: The function will not be called before `hold_time`, i.e. the button needs to be pressed this long to activate the function
* `SecondFunc`: Holding the button for at least `hold_time` will additionally execute the function `functionCall2` with `functionCall2Args`.
* `SecondFunc`: Pressing the button (shorter than `hold_time`) will execute the function `functionCall` with `functionCallArgs`. Holding the button for at least `hold_time` will execute the function `functionCall2` with `functionCall2Args`.
* `SecondFuncRepeat`: Like SecondFunc, but `functionCall2` is repeated after each `hold_time` interval.

In every `hold_mode` except `Postpone`, the main action `functionCall` gets executed instantly.

Holding the button even longer than `hold_time` will cause no further action unless you are in the `Repeat` or `SecondFuncRepeat` mode.

* **hold_time**: Reference time for this buttons `hold_mode` feature in seconds. Default is `0.3`. This setting is ignored if `hold_mode` is unset or `None`
Expand All @@ -79,10 +82,10 @@ However, a button has more parameters than these. In the following comprehensive
* `pull_off`. Use this to deactivate internal pull-up/pulldown resistors. This is useful if your wiring includes your own (external) pull up / down resistors.
* **edge**: Configures the events in which the GPIO library shall trigger the callback function. Valid settings:
* `falling` (Default). Triggers if the GPIO voltage goes down.
* `rising`. Trigegrs only if the GPIO voltage goes up.
* `rising`. Triggers only if the GPIO voltage goes up.
* `both`. Triggers in both cases.
* **bouncetime**: This is a setting of the GPIO library to limit bouncing effects during button usage. Default is `500` ms.
* **antibouncehack**: Despite the integrated bounce reduction of the GPIO library some users may notice false triggers of their buttons (e.g. unrequested / double actions when releasing the button). If you encounter such problems, try setting this setting to `True` to activate an additional countermeasure.
* **antibouncehack**: Despite the integrated bounce reduction of the GPIO library some users may notice false triggers of their buttons (e.g. unrequested / double actions when releasing the button). If you encounter such problems, try setting this to `True` to activate an additional countermeasure.

Note: If you prefer, you may also use `Type: SimpleButton` instead of `Type: Button` - this makes no difference.

Expand Down
4 changes: 2 additions & 2 deletions components/gpio_control/test/test_SimpleButton.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def test_hold_SecondFunc_longer_holdtime(self, simple_button):
simple_button.hold_mode = 'SecondFunc'
calls = mockedSecAction.call_count
simple_button.callbackFunctionHandler(simple_button.pin)
mockedAction.assert_called_once()
mockedAction.assert_not_called()
assert mockedSecAction.call_count - calls == 1

def test_hold_SecondFunc_shorter_holdtime(self, simple_button):
Expand All @@ -170,7 +170,7 @@ def test_hold_SecondFuncRepeat_longer_holdtime(self, simple_button):
simple_button.hold_mode = 'SecondFuncRepeat'
calls = mockedSecAction.call_count
simple_button.callbackFunctionHandler(simple_button.pin)
mockedAction.assert_called_once()
mockedAction.assert_not_called()
assert mockedSecAction.call_count - calls == 3

def test_hold_SecondFuncRepeat_shorter_holdtime(self, simple_button):
Expand Down