diff --git a/adafruit_debouncer.py b/adafruit_debouncer.py index 868a217..67037f7 100644 --- a/adafruit_debouncer.py +++ b/adafruit_debouncer.py @@ -31,20 +31,31 @@ from micropython import const from adafruit_ticks import ticks_ms, ticks_diff -_DEBOUNCED_STATE = const(0x01) -_UNSTABLE_STATE = const(0x02) -_CHANGED_STATE = const(0x04) +try: + from typing import Callable, Optional, Union + from circuitpython_typing.io import ROValueIO +except ImportError: + pass -_TICKS_PER_SEC = const(1000) +_DEBOUNCED_STATE: int = const(0x01) +_UNSTABLE_STATE: int = const(0x02) +_CHANGED_STATE: int = const(0x04) + +_TICKS_PER_SEC: int = const(1000) class Debouncer: """Debounce an input pin or an arbitrary predicate""" - def __init__(self, io_or_predicate, interval=0.010): + def __init__( + self, + io_or_predicate: Union[ROValueIO, Callable[[], bool]], + interval: float = 0.010, + ) -> None: """Make an instance. - :param DigitalInOut/function io_or_predicate: the DigitalIO or function to debounce - :param int interval: bounce threshold in seconds (default is 0.010, i.e. 10 milliseconds) + :param DigitalInOut/function io_or_predicate: the DigitalIO or + function that returns a boolean to debounce + :param float interval: bounce threshold in seconds (default is 0.010, i.e. 10 milliseconds) """ self.state = 0x00 if hasattr(io_or_predicate, "value"): @@ -59,21 +70,21 @@ def __init__(self, io_or_predicate, interval=0.010): # Could use the .interval setter, but pylint prefers that we explicitly # set the real underlying attribute: - self._interval_ticks = interval * _TICKS_PER_SEC + self._interval_ticks = int(interval * _TICKS_PER_SEC) - def _set_state(self, bits): + def _set_state(self, bits: int) -> None: self.state |= bits - def _unset_state(self, bits): + def _unset_state(self, bits: int) -> None: self.state &= ~bits - def _toggle_state(self, bits): + def _toggle_state(self, bits: int) -> None: self.state ^= bits - def _get_state(self, bits): + def _get_state(self, bits: int) -> bool: return (self.state & bits) != 0 - def update(self, new_state=None): + def update(self, new_state: Optional[int] = None) -> None: """Update the debouncer state. MUST be called frequently""" now_ticks = ticks_ms() self._unset_state(_CHANGED_STATE) @@ -96,38 +107,38 @@ def update(self, new_state=None): self._state_changed_ticks = now_ticks @property - def interval(self): + def interval(self) -> float: """The debounce delay, in seconds""" return self._interval_ticks / _TICKS_PER_SEC @interval.setter - def interval(self, new_interval_s): + def interval(self, new_interval_s: float) -> None: self._interval_ticks = new_interval_s * _TICKS_PER_SEC @property - def value(self): + def value(self) -> bool: """Return the current debounced value.""" return self._get_state(_DEBOUNCED_STATE) @property - def rose(self): + def rose(self) -> bool: """Return whether the debounced value went from low to high at the most recent update.""" return self._get_state(_DEBOUNCED_STATE) and self._get_state(_CHANGED_STATE) @property - def fell(self): + def fell(self) -> bool: """Return whether the debounced value went from high to low at the most recent update.""" return (not self._get_state(_DEBOUNCED_STATE)) and self._get_state( _CHANGED_STATE ) @property - def last_duration(self): + def last_duration(self) -> float: """Return the number of seconds the state was stable prior to the most recent transition.""" return self._last_duration_ticks / _TICKS_PER_SEC @property - def current_duration(self): + def current_duration(self) -> float: """Return the number of seconds since the most recent transition.""" return ticks_diff(ticks_ms(), self._state_changed_ticks) / _TICKS_PER_SEC @@ -148,12 +159,12 @@ class Button(Debouncer): def __init__( self, - pin, - short_duration_ms=200, - long_duration_ms=500, - value_when_pressed=False, + pin: Union[ROValueIO, Callable[[], bool]], + short_duration_ms: int = 200, + long_duration_ms: int = 500, + value_when_pressed: bool = False, **kwargs - ): + ) -> None: self.short_duration_ms = short_duration_ms self.long_duration_ms = long_duration_ms self.value_when_pressed = value_when_pressed @@ -165,20 +176,20 @@ def __init__( super().__init__(pin, **kwargs) @property - def pressed(self): + def pressed(self) -> bool: """Return whether the button was pressed or not at the last update.""" return (self.value_when_pressed and self.rose) or ( not self.value_when_pressed and self.fell ) @property - def released(self): + def released(self) -> bool: """Return whether the button was release or not at the last update.""" return (self.value_when_pressed and self.fell) or ( not self.value_when_pressed and self.rose ) - def update(self, new_state=None): + def update(self, new_state: Optional[int] = None) -> None: super().update(new_state) if self.pressed: self.last_change_ms = ticks_ms() @@ -210,12 +221,12 @@ def update(self, new_state=None): self.short_to_show = 0 @property - def short_count(self): + def short_count(self) -> int: """Return the number of short press if a series of short presses has ended at the last update.""" return self.short_to_show @property - def long_press(self): + def long_press(self) -> bool: """Return whether a long press has occured at the last update.""" return self.long_to_show