Skip to content

Commit f3b4b20

Browse files
Fix button SecondFunc trigger (MiczFlor#2367)
* fix instant execution of main action on holding * simplify holdMode callback handling * Update test_SimpleButton.py * update docs * fix: only go into while loop if further held * docs: update comments * docs: add alert for behavior change * docs: fix typo
1 parent cffa96c commit f3b4b20

File tree

3 files changed

+34
-26
lines changed

3 files changed

+34
-26
lines changed

components/gpio_control/GPIODevices/simple_button.py

+23-18
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,7 @@ def callbackFunctionHandler(self, *args):
106106
if inval != GPIO.LOW:
107107
return None
108108

109-
if self.hold_mode in ('Repeat', 'Postpone', 'SecondFunc', 'SecondFuncRepeat'):
110-
return self.longPressHandler(*args)
111-
else:
112-
logger.info('{}: execute callback'.format(self.name))
113-
return self.when_pressed(*args)
109+
return self._handleCallbackFunction(*args)
114110

115111
@property
116112
def when_pressed(self):
@@ -136,36 +132,45 @@ def when_pressed(self, func):
136132
def set_callbackFunction(self, callbackFunction):
137133
self.when_pressed = callbackFunction
138134

139-
def longPressHandler(self, *args):
140-
logger.info('{}: longPressHandler, mode: {}'.format(self.name, self.hold_mode))
141-
# instant action (except Postpone mode)
142-
if self.hold_mode != "Postpone":
143-
self.when_pressed(*args)
135+
def _handleCallbackFunction(self, *args):
136+
logger.info('{}: handleCallbackFunction, mode: {}'.format(self.name, self.hold_mode))
144137

145-
# action(s) after hold_time
146138
if self.hold_mode == "Repeat":
147-
# Repeated call of main action (multiple times if button is held long enough)
139+
# Instantly call primary action
140+
self.when_pressed(*args)
141+
# Repeated call of primary action if button is held long enough
148142
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
149143
self.when_pressed(*args)
150144

151145
elif self.hold_mode == "Postpone":
152-
# Postponed call of main action (once)
146+
# Postponed call of primary action (once)
153147
if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
154148
self.when_pressed(*args)
155-
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
156-
pass
149+
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
150+
pass
157151

158152
elif self.hold_mode == "SecondFunc":
159153
# Call of secondary action (once)
154+
# execute primary action if not held past hold_time
160155
if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
161156
self.when_held(*args)
162-
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
163-
pass
157+
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
158+
pass
159+
else:
160+
self.when_pressed(*args)
164161

165162
elif self.hold_mode == "SecondFuncRepeat":
166163
# Repeated call of secondary action (multiple times if button is held long enough)
167-
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
164+
# execute primary action if not held past hold_time
165+
if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
168166
self.when_held(*args)
167+
while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW):
168+
self.when_held(*args)
169+
else:
170+
self.when_pressed(*args)
171+
172+
else:
173+
self.when_pressed(*args)
169174

170175
def __del__(self):
171176
logger.debug('remove event detection')

components/gpio_control/README.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,18 @@ functionCall: functionCallPlayerPause
5959
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:
6060

6161
* **functionCallArgs**: Arguments for primary function, defaults to `None`. Arguments are ignored, if `functionCall` does not take any.
62+
63+
> [!IMPORTANT]
64+
> 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.
65+
> 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`!
66+
> Existing configurations may need to adapt to this.
6267
* **hold_mode**: Specifies what shall happen if the button is held pressed for longer than `hold_time`:
6368
* `None` (Default): Nothing special will happen.
64-
* `Repeat`: The configured `functionCall` is repeated after each `hold_time` interval.
69+
* `Repeat`: The configured `functionCall` is instantly called and repeatedly after each `hold_time` interval.
6570
* `Postpone`: The function will not be called before `hold_time`, i.e. the button needs to be pressed this long to activate the function
66-
* `SecondFunc`: Holding the button for at least `hold_time` will additionally execute the function `functionCall2` with `functionCall2Args`.
71+
* `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`.
6772
* `SecondFuncRepeat`: Like SecondFunc, but `functionCall2` is repeated after each `hold_time` interval.
6873

69-
In every `hold_mode` except `Postpone`, the main action `functionCall` gets executed instantly.
70-
7174
Holding the button even longer than `hold_time` will cause no further action unless you are in the `Repeat` or `SecondFuncRepeat` mode.
7275

7376
* **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`
@@ -79,10 +82,10 @@ However, a button has more parameters than these. In the following comprehensive
7982
* `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.
8083
* **edge**: Configures the events in which the GPIO library shall trigger the callback function. Valid settings:
8184
* `falling` (Default). Triggers if the GPIO voltage goes down.
82-
* `rising`. Trigegrs only if the GPIO voltage goes up.
85+
* `rising`. Triggers only if the GPIO voltage goes up.
8386
* `both`. Triggers in both cases.
8487
* **bouncetime**: This is a setting of the GPIO library to limit bouncing effects during button usage. Default is `500` ms.
85-
* **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.
88+
* **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.
8689

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

components/gpio_control/test/test_SimpleButton.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def test_hold_SecondFunc_longer_holdtime(self, simple_button):
150150
simple_button.hold_mode = 'SecondFunc'
151151
calls = mockedSecAction.call_count
152152
simple_button.callbackFunctionHandler(simple_button.pin)
153-
mockedAction.assert_called_once()
153+
mockedAction.assert_not_called()
154154
assert mockedSecAction.call_count - calls == 1
155155

156156
def test_hold_SecondFunc_shorter_holdtime(self, simple_button):
@@ -170,7 +170,7 @@ def test_hold_SecondFuncRepeat_longer_holdtime(self, simple_button):
170170
simple_button.hold_mode = 'SecondFuncRepeat'
171171
calls = mockedSecAction.call_count
172172
simple_button.callbackFunctionHandler(simple_button.pin)
173-
mockedAction.assert_called_once()
173+
mockedAction.assert_not_called()
174174
assert mockedSecAction.call_count - calls == 3
175175

176176
def test_hold_SecondFuncRepeat_shorter_holdtime(self, simple_button):

0 commit comments

Comments
 (0)