Skip to main content

sshkeyboard

Project description

sshkeyboard

The only keyboard event callback library that works everywhere, even when used through an SSH connection (hence the name).

It works with headless computers and servers, or for example inside Windows Subsystem for Linux (WSL 2). One good use case is controlling Raspberry Pi based robots or RC cars through SSH. Note that this library can also be used locally without an SSH connection.

It does not depend on X server, uinput, root access (sudo) or any external dependencies.

Supports asyncio and sequential/concurrent callback modes. For Python 3.6+.

Documentation - Github source - PyPI - Reference - Downloads

Quick start

Installation:

pip install sshkeyboard

Simple example to fire events when a key is pressed or released. esc key ends listening by default:

from sshkeyboard import listen_keyboard

def press(key):
    print(f"'{key}' pressed")

def release(key):
    print(f"'{key}' released")

listen_keyboard(
    on_press=press,
    on_release=release,
)

Output:

$ python example.py
'a' pressed
'a' released

How it works

The sshkeyboard library works without X server and uinput.

On Unix based systems (such as Linux, macOS) it works by parsing characters from sys.stdin. This is done with fcntl and termios standard library modules.

On Windows msvcrt standard library module is used to read user input. The Windows support is still new, so please create an issue if you run into problems.

This behaviour allows it to work where other libraries like pynput or keyboard do not work, but it comes with some limitations, mainly:

  1. Holding multiple keys down at the same time does not work, the library releases the previous keys when a new one is pressed. Releasing keys also happens after a short delay, and some key presses can get lost if the same key gets spammed fast.
  2. Some keys do not write to sys.stdin when pressed, such as Ctrl, Shift, Caps Lock, Alt and Windows/Command/Super key. That is why this library does not attempt to parse those even if they could be technically be parsed in some cases

Advanced use

Sequential mode

Normally this library allows on_press and on_release callbacks to be run concurrently. This means that by running:

import time
from sshkeyboard import listen_keyboard

def press(key):
    print(f"'{key}' pressed")
    time.sleep(3)
    print(f"'{key}' slept")

listen_keyboard(on_press=press)

and pressing "a", "s" and "d" keys will log:

'a' pressed
's' pressed
'd' pressed
'a' slept
's' slept
'd' slept

But sometimes you don't want to allow the callbacks to overlap, then you should set sequential parameter to True:

# ...
listen_keyboard(
    on_press=press,
    sequential=True,
)

Then pressing "a", "s" and "d" keys will log:

'a' pressed
'a' slept
's' pressed
's' slept
'd' pressed
'd' slept

Asyncio

You can also use asynchronous functions as on_press / on_release callbacks with listen_keyboard:

import asyncio
from sshkeyboard import listen_keyboard

async def press(key):
    print(f"'{key}' pressed")
    await asyncio.sleep(3)
    print(f"'{key}' slept")

listen_keyboard(on_press=press)

NOTE remember to use await asyncio.sleep(...) in async callbacks instead of time.sleep(...) or the timings will fail:

Mixing asynchronous and concurrent callbacks

listen_keyboard also supports mixing asynchronous and concurrent callbacks:

import asyncio
import time
from sshkeyboard import listen_keyboard

async def press(key):
    print(f"'{key}' pressed")
    await asyncio.sleep(3)
    print(f"'{key}' press slept")

def release(key):
    print(f"'{key}' relased")
    time.sleep(3)
    print(f"'{key}' release slept")

listen_keyboard(
    on_press=press,
    on_release=release,
)

Here pressing "a" and "s" will log:

'a' pressed
'a' relased
's' pressed
's' relased
'a' press slept
's' press slept
'a' release slept
's' release slept

And with sequential=True:

# ...
listen_keyboard(
    on_press=press,
    on_release=release,
    sequential=True,
)

will log:

'a' pressed
'a' press slept
'a' relased
'a' release slept
's' pressed
's' press slept
's' relased
's' release slept

NOTE remember to use await asyncio.sleep(...) in async callbacks instead of time.sleep(...) or the timings will fail:

Stop listening

You can change the key that ends the listening by giving until parameter, which defaults to "esc":

# ...
listen_keyboard(
    on_press=press,
    until="space",
)

You also can manually stop listening by calling stop_listening() from the callback or from some other function:

from sshkeyboard import listen_keyboard, stop_listening

def press(key):
    print(f"'{key}' pressed")
    if key == "z":
        stop_listening()

listen_keyboard(on_press=press)

until can be also set to None. This means that listening ends only with stop_listening() or if an error has been raised.

Troubleshooting

If some keys do not seem to register correctly, try turning the debug mode on. This will add logs if some keys are skipped intentionally:

# ...
listen_keyboard(
    on_press=press,
    debug=True,
)

If one key press causes multiple on_press / on_release callbacks or if releasing happens too slowly, you can try to tweak the default timing parameters:

# ...
listen_keyboard(
    on_press=press,
    delay_second_char=0.75,
    delay_other_chars=0.05,
)

More

Check out the full reference for more functions and parameters such as:

  • lower parameter
  • sleep parameter
  • max_thread_pool_workers parameter
  • listen_keyboard_manual function

Direct links to functions:

Development

This sections explains how to build the documentation and how to run the pre-commit script locally. This helps if you want to create a pull request or if you just want to try things out.

Building the documentations allows you to build all of the files served on the documentation site locally.

The pre-commit script handles running tests, formatting and linting before each Git commit. These same checks also run automatically on Github Actions.

Start by cloning this library, and change directory to the project root:

git clone git@github.com:ollipal/sshkeyboard.git
cd sshkeyboard

Optionally, create and activate a virtual environment at the root of the project (you might need to use python3 keyword instead of python):

python -m venv .env
source .env/bin/activate

(Later you can deactivate the virtual environment with: deactivate)

To build the documentation or run the pre-commit script locally, you need to install the development dependencies:

pip install -r dev-requirements.txt

Documentation

To build the documentation locally, first change into docs/ directory:

cd docs

Then to build the documentation, call:

make html

Now you should have a new docs/build/ directory, and you can open <your-clone-path>/sshkeyboard/docs/build/html/index.html from your browser.

You can force the rebuild by running:

rm -rf build/ && make html

You can change the documentation content by changing README.md or files from src/ or docs/source/. If you are mainly changing contents from docs/source/, you can enable automatic re-building by running:

sphinx-autobuild ./source/ ./build/html/

Running the pre-commit script

You can run the tests (tox, pytest), formatting (black, isort) and linting (pflake8, pep8-naming, codespell, markdownlint) simply by executing:

./pre-commit

Now if you want to automatically run these when you call git commit, copy the script into .git/hooks/ directory:

cp pre-commit .git/hooks

NOTE: this process does not run markdownlint by default as it requires Ruby to be installed. If you want to run markdownlint locally as well, install Ruby and install markdown lint with gem install mdl -v 0.11.0. Then from pre-commit change RUN_MDL=false to RUN_MDL=true. (You need to copy the file again into .git/hooks/ if you did that earlier)

Comparison to other keyboard libraries

The other keyboard libraries work by reading proper keycodes from the system.

This means that they usually require either X server or uinput, so they do not work over SSH. But this means they do not have the same limitations as this library.

They usually can also support more features such as pressing the keys instead of just reacting to user input.

I have good experiences from these libraries:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sshkeyboard-2.3.1.tar.gz (20.3 kB view details)

Uploaded Source

Built Distribution

sshkeyboard-2.3.1-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

Details for the file sshkeyboard-2.3.1.tar.gz.

File metadata

  • Download URL: sshkeyboard-2.3.1.tar.gz
  • Upload date:
  • Size: 20.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.22.0

File hashes

Hashes for sshkeyboard-2.3.1.tar.gz
Algorithm Hash digest
SHA256 3273be5b2fde7f8d2ea075d40e1981104ac0928d7b77a848756f08d0e66a3d9f
MD5 f8c08df60ce10776b03130e81a71370c
BLAKE2b-256 e37bd78e6ade4bb4680d0a610ed02047c4de04db62de8864193bf842c59c47cb

See more details on using hashes here.

File details

Details for the file sshkeyboard-2.3.1-py3-none-any.whl.

File metadata

  • Download URL: sshkeyboard-2.3.1-py3-none-any.whl
  • Upload date:
  • Size: 12.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.22.0

File hashes

Hashes for sshkeyboard-2.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 05ec2cb116bd9c4a7c17d7add4a5af74e12eb2add24c110608cb90f6812692f9
MD5 10020d2b1e422de5f5cae6e1c0f598bd
BLAKE2b-256 eab6d24c6184348a91386e5ca5fe3e46668ef978dd694ff98574ef0dd5904522

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page