Source code for SimulRPi.pindb

"""Module that defines a database for storing information about GPIO pins.

The database is created as a dictionary mapping channel numbers to objects
representing GPIO pins.

The :class:`PinDB` class provides an API for accessing this database with
such functions as retrieving or setting pins' attributes.

"""
# NOTE: on Python 3.5 and 3.6, can't use ``import SimulRPi.GPIO as GPIO``
# AttributeError: module 'SimulRPi' has no attribute 'GPIO
# if circular import
import SimulRPi.GPIO


# TODO: change to Channel?
[docs]class Pin: """Class that represents a GPIO pin. Parameters ---------- channel_number : int GPIO channel number based on the numbering system you have specified (`BOARD` or `BCM`). channel_id : str Unique identifier. gpio_type : int Type of a GPIO channel: e.g. 1 (`GPIO.IN`) or 0 (`GPIO.OUT`). channel_name : str, optional It will be displayed in the terminal along with the LED symbol if it is available. Otherwise, the ``channel_number`` is shown. By default, its value is :obj:`None`. key : str or None, optional Keyboard key associated with the GPIO channel, e.g. `cmd_r`. led_symbols : dict, optional It should only be defined for output channels. It is a dictionary defining the symbols to be used when the LED is turned ON and OFF. If not found for an ouput channel, then the default LED symbols will be used as specified in :class:`SimulRPi.manager.Manager`. **Example**:: { "ON": "🔵", "OFF": "⚪ " } pull_up_down : int or None, optional Initial value of an input channel, e.g. `GPIO.PUP_UP`. Default value is :obj:`None`. initial : int or None, optional Initial value of an output channel, e.g. `GPIO.HIGH`. Default value is :obj:`None`. Attributes ---------- state : int State of the GPIO channel: 1 (`HIGH`) or 0 (`LOW`). """ def __init__(self, channel_number, channel_id, channel_type, channel_name=None, key=None, led_symbols=None, pull_up_down=None, initial=None): self.channel_number = channel_number self.channel_id = channel_id self.channel_type = channel_type self.channel_name = channel_name self.key = key self.pull_up_down = pull_up_down self.initial = initial self.led_symbols = led_symbols # TODO: check if setting of state is good if self.channel_type == SimulRPi.GPIO.IN: # Input channel (e.g. push button) self.state = self.initial if self.initial else SimulRPi.GPIO.HIGH else: # Output channel (e.g. LED) self.state = self.initial if self.initial else SimulRPi.GPIO.LOW
# TODO: change to ChannelDB?
[docs]class PinDB: """Class for storing and modifying :class:`Pin`\s. Each instance of :class:`Pin` is saved in a dictionary that maps its channel number to the :class:`Pin` object. Attributes ---------- output_pins : list List containing :class:`Pin` objects that are **output** channels. .. note:: The dictionary (a "database" of :class:`Pin`\s) must be accessed through the different methods available in :class:`PinDB`, e.g. :meth:`get_pin_from_channel`. """ def __init__(self): # Maps channel numbers to Pin objects self._pins = {} # TODO: explain more # Maps keyboard keys to Pin objects # NOTE: this dict is only for INPUT channels with an associated key self._key_to_pin_map = {} # List only for OUTPUT channels self.output_pins = []
[docs] def create_pin(self, channel_number, channel_id, channel_type, **kwargs): """Create an instance of :class:`Pin` and save it in a dictionary. Based on the given arguments, an instance of :class:`Pin` is created and added to a dictionary that acts like a database of pins with the key being the pin's channel number and the value is an instance of :class:`Pin`. Parameters ---------- channel_number : int GPIO channel number based on the numbering system you have specified (`BOARD` or `BCM`). channel_id : str Unique identifier. channel_type : int Type of a GPIO channel: e.g. 1 (`GPIO.IN`) or 0 (`GPIO.OUT`). kwargs : dict, optional These are the (optional) keyword arguments for ``Pin.__init__()``. See :class:`Pin` for a list of its parameters which can be included in ``kwargs``. Raises ------ KeyError Raised if two channels are using the same channel number. """ if self._pins.get(channel_number): # TODO: error or warning? Overwrite? raise KeyError("Duplicate channel numbers: {}".format(channel_number)) self._pins[channel_number] = Pin(channel_number, channel_id, channel_type, **kwargs) if channel_type == SimulRPi.GPIO.OUT: # Output channel (e.g. LED) # Save the output pin so the thread that displays LEDs knows what # pins are OUTPUT and therefore connected to LEDs. self.output_pins.append(self._pins[channel_number]) # Update the other internal dict if key is given if kwargs['key']: # Input channel (e.g. push button) # TODO: assert on channel_type which should be IN? self._key_to_pin_map[kwargs['key']] = self._pins[channel_number]
[docs] def get_pin_from_channel(self, channel_number): """Get a :class:`Pin` from a given channel. Parameters ---------- channel_number : int GPIO channel number associated with the :class:`Pin` to be retrieved. Returns ------- Pin : :class:`Pin` or :obj:`None` If no :class:`Pin` could be retrieved based on the given channel, :obj:`None` is returned. Otherwise, a :class:`Pin` object is returned. """ return self._pins.get(channel_number)
[docs] def get_pin_from_key(self, key): """Get a :class:`Pin` from a given pressed/released key. Parameters ---------- key : str The pressed/released key that is associated with the :class:`Pin` to be retrieved. Returns ------- Pin : :class:`Pin` or :obj:`None` If no :class:`Pin` could be retrieved based on the given key, :obj:`None` is returned. Otherwise, a :class:`Pin` object is returned. """ return self._key_to_pin_map.get(key)
[docs] def get_pin_state(self, channel_number): """Get a :class:`Pin`\'s state from a given channel. The state associated with a :class:`Pin` can either be 1 (`HIGH`) or 0 (`LOW`). Parameters ---------- channel_number : int GPIO channel number associated with the :class:`Pin` whose state is to be returned. Returns ------- state : :obj:`int` or :obj:`None` If no :class:`Pin` could be retrieved based on the given channel number, then :obj:`None` is returned. Otherwise, the :class:`Pin`\'s state is returned: 1 (`HIGH`) or 0 (`LOW`). """ pin = self._pins.get(channel_number) if pin: return pin.state else: return None
[docs] def set_pin_key_from_channel(self, channel_number, key): """Set a :class:`Pin`\'s key from a given channel. A :class:`Pin` is retrieved based on a given channel, then its ``key`` is set. Parameters ---------- channel_number : int GPIO channel number associated with the :class:`Pin` whose key will be set. key : str The new keyboard key that a :class:`Pin` will be updated with. Returns ------- retval : bool Returns `True` if the :class:`Pin` was successfully set with `key`. Otherwise, it returns `False`. """ pin = self.get_pin_from_channel(channel_number) if pin: old_key = pin.key pin.key = key # TODO: only update dict if the key is different from the actual # pin's key but then return True or False if no update? # if key != old_key: del self._key_to_pin_map[old_key] self._key_to_pin_map[key] = pin return True else: return False
[docs] def set_pin_name_from_channel(self, channel_number, channel_name): """Set a :class:`Pin`\'s channel name from a given channel number. A :class:`Pin` is retrieved based on a given channel, then its ``channel_name`` is set. Parameters ---------- channel_number : int GPIO channel number associated with the :class:`Pin` whose channel name will be set. channel_name : str The new channel name that a :class:`Pin` will be updated with. Returns ------- retval : bool Returns `True` if the :class:`Pin` was successfully set with `channel_name`. Otherwise, it returns `False`. """ pin = self.get_pin_from_channel(channel_number) if pin: # TODO: only update name if the name is different from the actual pin.channel_name = channel_name return True else: return False
[docs] def set_pin_id_from_channel(self, channel_number, channel_id): """Set a :class:`Pin`\'s channel id from a given channel number. A :class:`Pin` is retrieved based on a given channel, then its ``channel_id`` is set. Parameters ---------- channel_number : int GPIO channel number associated with the :class:`Pin` whose channel id will be set. channel_id : str The new channel id that a :class:`Pin` will be updated with. Returns ------- retval : bool Returns `True` if the :class:`Pin` was successfully set with `channel_id`. Otherwise, it returns `False`. """ pin = self.get_pin_from_channel(channel_number) if pin: # TODO: only update id if the id is different from the actual pin.channel_id = channel_id return True else: return False
[docs] def set_pin_state_from_channel(self, channel_number, state): """Set a :class:`Pin`\'s state from a given channel. A :class:`Pin` is retrieved based on a given channel, then its ``state`` is set. Parameters ---------- channel_number : int GPIO channel number associated with the :class:`Pin` whose state will be set. state : int State the GPIO channel should take: 1 (`HIGH`) or 0 (`LOW`). Returns ------- retval : bool Returns `True` if the :class:`Pin` was successfully set with `state`. Otherwise, it returns `False`. """ pin = self.get_pin_from_channel(channel_number) if pin: # TODO: only update state if the state is different from the actual pin.state = state return True else: return False
[docs] def set_pin_state_from_key(self, key, state): """Set a :class:`Pin`\'s state from a given key. A :class:`Pin` is retrieved based on a given key, then its ``state`` is set. Parameters ---------- key : str The keyboard key associated with the :class:`Pin` whose state will be set. state : int State the GPIO channel should take: 1 (`HIGH`) or 0 (`LOW`). Returns ------- retval : bool Returns `True` if the :class:`Pin` was successfully set with `state`. Otherwise, it returns `False`. """ pin = self.get_pin_from_key(key) if pin: # TODO: only update state if the state is different from the actual pin.state = state return True else: return False
[docs] def set_pin_symbols_from_channel(self, channel_number, led_symbols): """Set a :class:`Pin`\'s led symbols from a given channel. A :class:`Pin` is retrieved based on a given key, then its ``led_symbols`` is set. Parameters ---------- channel_number : int GPIO channel number associated with the :class:`Pin` whose state will be set. led_symbols : dict It is a dictionary defining the symbols to be used when the LED is turned ON and OFF. See :class:`Pin` for more info about this attribute. Returns ------- retval : bool Returns `True` if the :class:`Pin` was successfully set with `led_symbols`. Otherwise, it returns `False`. """ pin = self.get_pin_from_channel(channel_number) if pin: # TODO: only update symbols if the symbols is different from the actual pin.led_symbols = led_symbols return True else: return False