Skip to content

About

Python Library for xs4all USB IP Phone

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

xs4all-handset

A Python library for the XS4ALL USB telephone handset on Linux (0c45:182e, Microdia / GENERIC USB Headset).

XS4ALL USB telephone handset with yellow controls and a numeric keypad

Photo by Mendel Mobach.

Read keypad events, control the ringer and call mode, and record or play audio without the original Windows softphone. The handset has been tested with Linux's snd-usb-audio and hid-generic drivers and PipeWire.

Requirements

  • Linux and Python 3.9 or later.
  • Access to the handset's hidraw interface; a udev rule is included.
  • For the optional audio helpers: parec, paplay, and PulseAudio or PipeWire's PulseAudio server.

There are no third-party Python runtime dependencies.

Install

Clone the repository and install it:

git clone https://github.com/Hack42/xs4all_handset.git
cd xs4all_handset
python3 -m venv .venv
. .venv/bin/activate
python -m pip install .

To use the library in another project's virtual environment:

python -m pip install /path/to/xs4all-handset

Or install directly from GitHub in your project's environment:

python -m pip install "git+https://github.com/Hack42/xs4all_handset.git"

Install the udev rule once on each machine:

sudo install -m 0644 70-xs4all-handset.rules /etc/udev/rules.d/70-xs4all-handset.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --action=change --subsystem-match=hidraw

Reconnect the handset if necessary. The rule gives the active local session access to this device and creates /dev/xs4all-handset. Python does not need to run as root. For headless services, configure appropriate device access. The rule is also available after package installation with xs4all-handset udev-rule.

Read buttons

from xs4all_handset import Handset

with Handset(talk=True) as phone:
    for event in phone.events():
        print(event.key, event.muted)

Initialization and report acknowledgements are automatic. talk=True enables call-dependent buttons such as mute; it does not make a telephone call. Leaving the context stops a ringer/call mode enabled through this connection and closes the device.

For integration into an existing loop, use phone.read_key(timeout=0.1). It returns None if no key arrives. Events are immutable dataclasses; event.to_dict() provides a JSON-compatible representation.

Button event.key Notes
Digits, star, hash 0–9, *, # Tested on hardware
Up / down up / down Codes e9 / ea
Call / answer call Idle-mode reports 53 + 31
Hang up hangup Idle-mode reports 41 or 40 + 54
Mute mute Call mode only; event.muted is True or False
Mail mail Reports 53 + bf
Redial recall Reports 40 + d0; historical API name
Lower / higher lower / higher Codes bd / bc

Compound protocol reports produce one key action. Unknown key codes remain available as unknown_0xNN. Applications choose what the buttons do: events do not automatically change system volume, mute audio, or inject keystrokes.

Ring

from xs4all_handset import Handset

with Handset() as phone:
    phone.ring(seconds=1)

For an event loop, use phone.start_ringing() and phone.stop_ringing(). phone.set_call_active(True) enables call mode without placing a call.

Audio

from xs4all_handset import AudioDevice

audio = AudioDevice()
recording = audio.record(5)
audio.play(recording)

The default is mono, 48 kHz, signed 16-bit little-endian PCM. WAV files are supported with audio.record_wav("voice.wav", 5) and audio.play_wav("voice.wav"). These helpers block; applications needing streaming may also use their usual Linux audio library.

AudioDevice(source=..., sink=..., sample_rate=..., channels=...) selects custom devices and formats. With multiple identical handsets, select the correct audio source and sink explicitly. Global audio defaults are unchanged.

Command line

xs4all-handset info
xs4all-handset monitor --talk --keys-only
xs4all-handset ring --seconds 1
xs4all-audio-test tone
xs4all-audio-test echo --seconds 5

python -m xs4all_handset provides the same handset commands. Diagnostic options include monitor --passive (read-only) and --seconds N.

Documentation and development

  • API reference: lifecycle, timeouts, errors, and integration.
  • Examples: button monitoring and audio echo.
  • Protocol notes: USB descriptors, reverse-engineering evidence, captured reports, and hardware validation.

From the repository root:

python -m pip install -e .
python -m unittest discover -v
python -m pip wheel --no-deps --wheel-dir dist .

Tests use captured handset reports, local sockets, and short-lived child processes; they do not require USB hardware or an audio server.

The observed keypad, microphone, earpiece, and ringer have been tested on hardware. Some firmware behavior remains based on static inspection; see the protocol notes. Keep reading events to acknowledge handset traffic. Use each Handset from one thread. Reopen the connection after unplugging and reconnecting; reconnect is not automatic.

License

Copyright (C) 2026 Mendel Mobach.

The library is licensed under the GNU General Public License version 3 only (GPL-3.0-only). See LICENSE for the full text.

About

Python Library for xs4all USB IP Phone

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages