# AITPI
Arbitrary Input for Terminal or a Pi, or Aitpi (pronounced 'eight pi')

# Goal
The goal of this project is to provide a simple, but arbitrary, input
mechanism for use with a raspberry pi, or a terminal keyboard (maybe more SBCs in the future?!).

This program can be configured with two simple json files.

# Supported
The project supports:
- Simple 'buttons'
    - '1 to 1' gpio to button setup on a raspberry pi
    - Non interrupt based key input
    - Interrupt based key input (using pynput)
- Encoders
    - '2 to 1' gpio to encoder setup on a raspberry pi
    - Non interrupt based 2 to 1 key input
    - Interrupt based 2 to 1 key input (using pynput)

# Examples
To configure your setup, you can create up to three types of json files:

## Command Registry:
A registry of commands that will interact directly with your user program
```
{
    "type0": {
        "commandName0": {
            "input_type": "button",
            "id": "0"
        },
        "commandName1": {
            "input_type": "button",
            "id": "1"
        }
    },
    "type1": {
        "commandName2": {
            "input_type": "encoder",
            "id": "2"
        }
    }
}
```
- The first layer of this json defines what 'type' each command is. You can use this to sort your commands in a meaningful way.
    - NOTE: Currently, you need a single type layer and cannot have more. This will be remedied in the future to allow 'foldered' types
- Each command is listed with a name, and a corrosponding dictionary.
    - Each command name must be unique regardless of type (this will be remedied once foldered types are implemented)
    - Each command must have an 'input_type' and 'id' attribute
        - 'input_type' lets Aitpi know what type of input this can be connected to
            - Valid input_types: 'encoder', 'button'
        - 'id' is the message id that the command events will be sent over
            - Valid ids: Any positive int, negative ints are reserved for Aitpi and could produce bad side effects

## Input list
The list of all 'input units' that your system uses
```
[
    {
        "name": "Button0",
        "type": "button",
        "mechanism": "rpi_gpio",
        "trigger": "5",
        "reg_link": "commandName0"
    },
    {
        "name": "Encoder0",
        "type": "encoder",
        "mechanism": "rpi_gpio",
        "left_trigger": "17",
        "right_trigger": "24",
        "reg_link": "commandName2"
    }
]
```
- This is an array of depth 1, with all your 'input units' listed as dictionaries inside
    - "name": specifies the name of the input unit
        - Valid names: Any string, must be unique among all input units
    - "type": specifies what type of input this unit is
        - Valid types: 'button', 'encoder'
    - "mechanism": This tells Aitpi by what mechanism the input will be watched
        - Valid mechanisms: 'key_interrupt', 'key_input', 'rpi_gpio'
            - key_interrupt: Uses [pynput](https://pypi.org/project/pynput/) to set interrupts on your keyboard itself
            - key_input: Manual in-code input through the function 'aitpi.takeInput'
            - rpi_gpio: Raspberry pi GPIO input, all input units are assumed to be active low
    - "trigger": The key string or gpio number that will trigger input for a button
        - NOTE: This is only needed if 'type' equals 'button'
        - Valid triggers: Any string, or any valid unused gpio number on a raspberry pi
            - Note strings of more than one char will not work with key_interrupt (pynput)
    - "left_trigger" and "right_trigger: The key string or gpio numbers that will act as a left or right for an encoder
        - NOTE: These are only needed if 'type' equals 'encoder'
        - Valid left_triggers and right_triggers: Any string, or any valid unused gpio number on a raspberry pi
            - Note strings of more than one char will not work with key_interrupt (pynput)
    - "reg_link": This corrosponds to a command from the command registry and will determine what message is sent to your user program

## Foldered Commands
Foldered commands allows you to consider all the files in a folder as a 'command' in the registry.
This uses the [watchdog](https://pythonhosted.org/watchdog/) python package to monitor folders and update on the fly.
All commands added will be deleted and reloaded upon program startup.
```
[
    {
        "name": "Folder0",
        "path": "/path/to/your/folder",
        "type": "<registry_type>",
        "id": "3",
        "input_type": "button"
    },
    {
        "name": "Folder1",
        "path": "/another/path",
        "type": "<registry_type>",
        "id": "4",
        "input_type": "encoder"
    }
]
```
- This is an array of depth 1 that lists all the folders you want to add
    - "name": Gives a name that you can use to access the json using 'getFolderedCommands'
        - Valid names: Any string
    - "path": Specifies the folder that will be watched
        - Valid paths: Any valid folder on your system
    - "type": This will tell Aitpi where to insert the commands from the folder into your command registry
        - Valid types: Any string
    - "id": When a command is added from the folder, this id will be the command registry 'id' value
        - Valid ids: Any positive int, negative ints are reserved for Aitpi and could produce bad side effects
    - "input_type": When a command is added from the folder, this directly corrosponds to the command registry's 'input_type'


# Example usage:
```python

# import the base aitpi
import aitpi

# The postal service allows us to receive messages
from aitpi import router

# In order to receive messages we must have an object with a consume(message) function
# This does not need to be a class.
# This is a simple example of how to implement a 'consumer'
class Watcher():
    def consume(self, message):
        print("Got command: %s" % message.name)
        print("On event: %s" % message.event)
        print("All attributes: %s" % message.attributes)

watcher = Watcher()

# Here we add a consumer that will receive commands with ids 0,1,2,3,4, these ids are the sameconsume
# as defined in your registry json file.consume
router.addConsumer([0,1,2,3,4], watcher)

# We must first initialize our command registry before we can start getting input
aitpi.addRegistry("<path_to_json>/command_reg.json", "<path_to_json>/foldered_commands.json")

# We can add multiple registries, and do not need the foldered commands
aitpi.addRegistry("<path_to_json>/another_reg.json")

# Once we initialize our system, all interrupt based commands can be sent imediately.
# Therefore, make sure you are ready to handle any input in your functions before calling this.
aitpi.initInput("<path_to_json>/example_input.json")

# For synchronous input (not interrupt based) using the 'key_input' input mechanism is desireable
# You can setup a custom progromatic form of input using this (If it is good enough, add it to AITPI!)
while (True):
    aitpi.takeInput(input())
```