Library

JLIP-specific functionality.

class vcrtool.jlip.BandInfo(*values)

Band information codes.

BROADCAST_SATELLITE = 64

Broadcast satellite.

TERRESTRIAL_BROADCAST = 48

Terrestrial broadcast.

class vcrtool.jlip.CommandResponse(checksum: int, raw: bytes, return_data: bytes, status: CommandStatus, tuple: CommandResponseTuple)

Command response information.

checksum : int

Checksum.

static from_bytes(resp: bytes) CommandResponse

Initialise from bytes.

Parameters:
resp : bytes

Raw response bytes.

Returns:

Parsed command response.

Return type:

CommandResponse

raw : bytes

Raw response.

return_data : bytes

Return data.

status : CommandStatus

Command status.

tuple : CommandResponseTuple

Lower-level command response information.

class vcrtool.jlip.CommandResponseTuple(jlip_id: int, command_status: CommandStatus, return_data: bytes)

Lower-level command response information.

command_status : CommandStatus

Command status.

jlip_id : int

JLIP ID.

return_data : bytes

Return data.

class vcrtool.jlip.CommandStatus(*values)

JLIP command status codes.

COMMAND_ACCEPTED = 3

Command accepted.

COMMAND_ACCEPTED_NOT_COMPLETE = 4

Command accepted but not complete.

COMMAND_NOT_IMPLEMENTED = 1

Command not implemented.

COMMAND_NOT_POSSIBLE = 5

Command not possible.

class vcrtool.jlip.DeviceNameResponse(checksum: int, raw: bytes, return_data: bytes, status: CommandStatus, tuple: CommandResponseTuple, name: str)

Device name response.

static from_bytes(resp: bytes) DeviceNameResponse

Initialise from bytes.

Parameters:
resp : bytes

Raw response bytes.

Returns:

Parsed device name response.

Return type:

DeviceNameResponse

name : str

Device name.

class vcrtool.jlip.JLIPTransport(serial_path: str, *, jlip_id: int = 1, raise_on_error_response: bool = True)

Send commands to HR-S9600U VCRs and similar devices over JLIP.

The serial port is the only I/O this class performs; the request framing and response validation live in JLIPCodec.

References

codec

The sans-I/O codec used to build and validate frames.

comm

Serial port object.

eject() CommandResponse

Eject the tape.

Returns:

Command response.

Return type:

CommandResponse

eject_wait() CommandResponse

Eject the tape and wait until it is done.

Returns:

Command response.

Return type:

CommandResponse

fast_forward() CommandResponse

Fast forward the tape.

Returns:

Command response.

Return type:

CommandResponse

fast_play_backward() CommandResponse

Fast play backward.

Returns:

Command response.

Return type:

CommandResponse

fast_play_forward() CommandResponse

Fast play forward.

Returns:

Command response.

Return type:

CommandResponse

frame_step() CommandResponse

Move forward one frame.

Returns:

Command response.

Return type:

CommandResponse

frame_step_back() CommandResponse

Move back one frame.

Returns:

Command response.

Return type:

CommandResponse

get_baud_rate_supported() CommandResponse

Get the baud rate supported by the device.

0x21 is returned, meaning 19200 baud, but this cannot be trusted.

Returns:

Command response.

Return type:

CommandResponse

get_device_code() CommandResponse

Get the device code.

Returns:

Command response.

Return type:

CommandResponse

get_device_name() CommandResponse

Get the device name.

Returns:

Command response.

Return type:

CommandResponse

get_input() CommandResponse

Get the input of the device.

Returns:

Command response.

Return type:

CommandResponse

get_machine_code() CommandResponse

Get the machine code.

Returns:

Command response.

Return type:

CommandResponse

get_play_speed() CommandResponse

Get playback speed.

Known responses in the first data field:

  • 0x67 means playing backward quickly.

  • 0x6D means paused or frame advancing.

  • 0x75 means normal.

  • 0x77 means playing forward quickly.

  • 0x7F is returned when inapplicable.

Returns:

Command response.

Return type:

CommandResponse

get_power_state() PowerStateResponse

Get the power state of the device.

Returns:

Power state response.

Return type:

PowerStateResponse

get_tuner_mode() VTUModeResponse

Get the tuner mode.

Returns:

Tuner mode response.

Return type:

VTUModeResponse

get_vtr_mode(*, fast: bool = False) VTRModeResponse

Get the VTR mode.

Parameters:
fast : bool

Use faster rate limit.

Returns:

VTR mode response.

Return type:

VTRModeResponse

jlip_id

JLIP ID.

nop() CommandResponse

No operation command.

Returns:

Command response.

Return type:

CommandResponse

pause() CommandResponse

Pause playback.

Returns:

Command response.

Return type:

CommandResponse

pause_recording() CommandResponse

Pause recording.

Returns:

Command response.

Return type:

CommandResponse

play() CommandResponse

Start playback.

Returns:

Command response.

Return type:

CommandResponse

presence_check() CommandResponse

Check if the device is present and responding.

Returns:

Command response.

Return type:

CommandResponse

preset_channel_down() CommandResponse

Change to the previous preset channel.

Returns:

Command response.

Return type:

CommandResponse

preset_channel_up() CommandResponse

Change to the next preset channel.

Returns:

Command response.

Return type:

CommandResponse

raise_on_error_response

Raise on error response.

real_channel_down() CommandResponse

Change to the previous channel.

Returns:

Command response.

Return type:

CommandResponse

real_channel_up() CommandResponse

Change to the next channel.

Returns:

Command response.

Return type:

CommandResponse

record() CommandResponse

Start recording.

Returns:

Command response.

Return type:

CommandResponse

reset_counter() CommandResponse

Reset the timecode counter.

Returns:

Command response.

Return type:

CommandResponse

rewind() CommandResponse

Rewind the tape.

Returns:

Command response.

Return type:

CommandResponse

rewind_wait() CommandResponse

Rewind the tape and wait until it is done.

Returns:

Command response.

Return type:

CommandResponse

select_band(n: int) CommandResponse

Select a band.

Parameters:
n : int

Band value.

Returns:

Command response.

Return type:

CommandResponse

select_preset_channel(n: int, nn: int, nnn: int) CommandResponse

Select a preset channel.

Parameters:
n : int

First channel parameter.

nn : int

Second channel parameter.

nnn : int

Third channel parameter.

Returns:

Command response.

Return type:

CommandResponse

select_real_channel(n: int, nn: int, nnn: int) CommandResponse

Select a channel.

Parameters:
n : int

First channel parameter.

nn : int

Second channel parameter.

nnn : int

Third channel parameter.

Returns:

Command response.

Return type:

CommandResponse

send_command(*args: int) bytes

Send a command at a slower rate limit.

This will raise pyrate_limiter’s pyrate_limiter.BucketFullException if the rate limit is exceeded.

Parameters:
*args : int

Command bytes to send.

Returns:

Raw response bytes.

Return type:

bytes

send_command_base(*args: int) bytes

Send a command (base method).

Parameters:
*args : int

Command bytes to send.

Returns:

Raw response bytes.

Return type:

bytes

send_command_fast(*args: int) bytes

Send a command with a faster rate limit.

This will raise pyrate_limiter’s pyrate_limiter.BucketFullException if the rate limit is exceeded.

Parameters:
*args : int

Command bytes to send.

Returns:

Raw response bytes.

Return type:

bytes

set_channel(channel: int) CommandResponse

Set the channel to a specific value.

Parameters:
channel : int

Channel number.

Returns:

Command response.

Return type:

CommandResponse

set_input(n: int, nn: int) CommandResponse

Set the input to a specific value.

Parameters:
n : int

Input number.

nn : int

Input sub-number.

Returns:

Command response.

Return type:

CommandResponse

set_jlip_id(n: int) CommandResponse

Set the JLIP ID of the device.

Parameters:
n : int

JLIP ID to set (1-99).

Returns:

Command response.

Return type:

CommandResponse

Raises:

ValueError – If the ID is not between 1 and 99.

set_record_mode(n: int) CommandResponse

Set the recording mode.

Parameters:
n : int

Record mode value.

Returns:

Command response.

Return type:

CommandResponse

set_record_speed(n: int) CommandResponse

Set the recording speed.

Parameters:
n : int

Record speed value.

Returns:

Command response.

Return type:

CommandResponse

slow_play_backward() CommandResponse

Slow play backward.

Returns:

Command response.

Return type:

CommandResponse

slow_play_forward() CommandResponse

Slow play forward.

Returns:

Command response.

Return type:

CommandResponse

stop() CommandResponse

Stop playback or recording.

Returns:

Command response.

Return type:

CommandResponse

turn_off() CommandResponse

Turn the device off.

Returns:

Command response.

Return type:

CommandResponse

turn_on() CommandResponse

Turn the device on.

Returns:

Command response.

Return type:

CommandResponse

class vcrtool.jlip.PowerStateResponse(checksum: int, raw: bytes, return_data: bytes, status: CommandStatus, tuple: CommandResponseTuple, is_on: bool)

Power state response.

static from_bytes(resp: bytes) PowerStateResponse

Initialise from bytes.

Parameters:
resp : bytes

Raw response bytes.

Returns:

Parsed power state response.

Return type:

PowerStateResponse

is_on : bool

If device is on.

class vcrtool.jlip.VTRMode(*values)

VTR mode codes.

EJECT = 0

Eject.

FF = 2

Fast forward.

NO_MODE = 15

No mode.

PAUSE = 7

Pause.

PLAY_BWD = 5

Play backward.

PLAY_FWD = 6

Play forward.

REC = 14

Record.

REC_PAUSE = 13

Record pause.

REW = 3

Rewind.

STOP = 1

Stop.

class vcrtool.jlip.VTRModeResponse(checksum: int, raw: bytes, return_data: bytes, status: CommandStatus, tuple: CommandResponseTuple, drop_frame_mode_enabled: bool, framerate: int, hour: int, minute: int, second: int, frame: int, is_ntsc: bool, is_pal: bool, recordable: bool, tape_inserted: bool, vtr_mode: VTRMode)

VTR mode response information.

drop_frame_mode_enabled : bool

Drop frame mode enabled.

frame : int

Frame number.

framerate : int

Framerate.

static from_bytes(resp: bytes) VTRModeResponse

Initialise from bytes.

Parameters:
resp : bytes

Raw response bytes.

Returns:

Parsed VTR mode response.

Return type:

VTRModeResponse

hour : int

Hour.

is_ntsc : bool

Is NTSC.

is_pal : bool

Is PAL.

minute : int

Minute.

recordable : bool

Recordable.

second : int

Second.

tape_inserted : bool

Tape inserted.

vtr_mode : VTRMode

VTR mode.

Sans-I/O protocol layer for the devices this package controls.

The classes here contain pure framing logic and perform no input or output. SIRCSCodec turns a SIRCSCommand into a tuple of Pulse intervals (the modulated carrier “marks” and silent “spaces” of an infrared frame) and reverses the process, while JLIPCodec builds JLIP request frames and validates response frames. The transport classes in vcrtool.sircs and vcrtool.jlip drive the actual hardware using the bytes and pulses produced here, which keeps the protocol logic trivially testable without devices or real-time sleeping.

The SIRCS timing values follow the canonical Sony specification where every interval is a multiple of a single unit T of 600 microseconds, and each frame is padded to a fixed 45 millisecond period.

vcrtool.sansio.CARRIER_FREQUENCY_HZ

Nominal infrared carrier frequency in hertz.

class vcrtool.sansio.CommandStatus(*values)

JLIP command status codes.

COMMAND_ACCEPTED = 3

Command accepted.

COMMAND_ACCEPTED_NOT_COMPLETE = 4

Command accepted but not complete.

COMMAND_NOT_IMPLEMENTED = 1

Command not implemented.

COMMAND_NOT_POSSIBLE = 5

Command not possible.

vcrtool.sansio.FRAME_DURATION_US

Total period of a single SIRCS frame in microseconds. The trailing space is stretched to it.

class vcrtool.sansio.JLIPCodec

Sans-I/O builder and validator for JLIP command frames.

static build_command(jlip_id: int, *args: int) bytes

Build the bytes of a JLIP request frame.

Parameters:
jlip_id : int

The JLIP ID of the target device.

*args : int

The command bytes, right-padded with zeros to fill the frame.

Returns:

The eleven-byte frame, including its trailing checksum.

Return type:

bytes

static validate_response(data: bytes, *, raise_on_error: bool = True) bytes

Validate a JLIP response frame.

Parameters:
data : bytes

The eleven-byte response frame.

raise_on_error : bool

If True, raise when the device reports an unaccepted status.

Returns:

The response frame unchanged.

Return type:

bytes

Raises:

ValueError – If the checksum does not match or, when raise_on_error is True, the command status is not accepted.

vcrtool.sansio.ONE_MARK_US

Duration of the mark that encodes a logical one, in microseconds.

class vcrtool.sansio.Pulse(carrier_on: bool, duration_us: int)

A single carrier interval that makes up part of a SIRCS frame.

carrier_on : bool

Whether the carrier is modulated (a mark) or silent (a space).

duration_us : int

Duration of the interval in microseconds.

class vcrtool.sansio.SIRCSCodec

Sans-I/O encoder and decoder for SIRCS infrared frames.

static decode(pulses: Iterable[Pulse]) SIRCSCommand

Decode the first frame of a pulse train back into a command.

Only the marks are inspected; spaces are ignored because their durations carry no information beyond separating marks. The variant is inferred from the number of data bits found. Trailing pulses beyond the first complete frame are ignored, which lets a captured repeated signal be decoded directly.

Parameters:
pulses : Iterable[Pulse]

The marks and spaces to decode.

Returns:

The recovered payload.

Return type:

SIRCSCommand

Raises:

ValueError – If no start mark is present, a mark does not match a known duration, or the number of data bits does not correspond to a known variant.

static encode(command: SIRCSCommand, *, repeat: int = 1) tuple[Pulse, ...]

Encode a command into the pulse train of one or more frames.

Bits are transmitted least-significant first, ordered command, then address, then extended field. Every frame begins with a start mark and is padded with a final space so that its total duration equals FRAME_DURATION_US.

Parameters:
command : SIRCSCommand

The payload to encode.

repeat : int

Number of identical frames to emit back to back. Real receivers expect at least three.

Returns:

The marks and spaces for repeat consecutive frames.

Return type:

tuple[Pulse, …]

Raises:

ValueError – If repeat is less than one or any field does not fit in its variant’s bit width.

class vcrtool.sansio.SIRCSCommand(command: int, address: int, extended: int = 0, variant: SIRCSVariant = SIRCSVariant.TWELVE_BIT)

A SIRCS payload independent of its wire representation.

address : int

The device address.

command : int

The 7-bit command (button) code.

extended : int

The extended field. Only meaningful for SIRCSVariant.TWENTY_BIT.

variant : SIRCSVariant

The frame width that determines how the fields are packed.

class vcrtool.sansio.SIRCSVariant(*values)

A SIRCS frame width.

Each member’s value is a (command_bits, address_bits, extended_bits) tuple describing how the payload is partitioned.

FIFTEEN_BIT = (7, 8, 0)

a 7-bit command and an 8-bit address.

Type:

Fifteen-bit frame

TWELVE_BIT = (7, 5, 0)

a 7-bit command and a 5-bit address.

Type:

Twelve-bit frame

TWENTY_BIT = (7, 5, 8)

a 7-bit command, a 5-bit address, and an 8-bit extended field.

Type:

Twenty-bit frame

property address_bits : int

Number of address bits in this variant.

Return type:

int

property command_bits : int

Number of command bits in this variant.

Return type:

int

property extended_bits : int

Number of extended bits in this variant.

Return type:

int

property total_bits : int

Total number of payload bits in this variant.

Return type:

int

vcrtool.sansio.SPACE_US

Duration of the silent space that separates every mark, in microseconds.

vcrtool.sansio.START_MARK_US

Duration of the leading start (header) mark in microseconds.

vcrtool.sansio.UNIT_US

Base SIRCS timing unit T in microseconds. Every other interval is a multiple of this value.

vcrtool.sansio.ZERO_MARK_US

Duration of the mark that encodes a logical zero, in microseconds.

vcrtool.sansio.checksum(vals: collections.abc.Sequence[int]) int

Compute the checksum for a JLIP frame.

Parameters:
vals : Sequence[int]

The first ten bytes of the frame to checksum.

Returns:

The computed checksum.

Return type:

int

SIRCS (Sony Infrared Remote Control System) transport over a Raspberry Pi Pico.

class vcrtool.sircs.PicoSIRCSTransport(serial_path: str, *, baud_rate: int = 115200, invert: bool = False)

Transport that delegates SIRCS timing to a Raspberry Pi Pico over USB serial.

The protocol framing lives in SIRCSCodec. This class serialises the pulse train it produces into a compact message and writes it to the Pico, whose firmware clocks the pin edges out with a PIO state machine for jitter-free timing. The PC therefore keeps all protocol knowledge and the Pico stays a dumb, precisely-timed output peripheral.

The message is a sync byte, a 16-bit big-endian pulse count, and then one three-byte record per pulse: a level byte followed by a 16-bit big-endian duration in microseconds. The level is the final pin state, so invert (used for an active-low CONTROL S jack) is resolved here and the firmware simply holds the pin at the given level.

codec

The sans-I/O codec used to build pulse trains.

comm

Serial connection to the Pico.

send_command(command: SIRCSCommand, *, repeat: int = 3) None

Encode a command and send it to the Pico.

Parameters:
command : SIRCSCommand

The payload to send.

repeat : int

Number of identical frames to play. Sony receivers expect at least three.

static serialize(pulses: Iterable[Pulse], *, invert: bool = False) bytes

Serialise a pulse train into the Pico wire message.

Parameters:
pulses : Iterable[Pulse]

The marks and spaces to serialise.

invert : bool

If True, mark and space levels are swapped.

Returns:

The complete message, ready to write to the Pico.

Return type:

bytes

Raises:

ValueError – If any pulse duration or the pulse count does not fit in 16 bits.

transmit(pulses: Iterable[Pulse]) None

Serialise a pulse train and write it to the Pico.

Parameters:
pulses : Iterable[Pulse]

The marks and spaces to transmit.

Utilities.

async vcrtool.utils.adebug_create_subprocess_exec(*args: Any, **kwargs: Any) Process

Run a subprocess and log the command at the logging.DEBUG level.

Return type:

asp.CompletedProcess[Any]

async vcrtool.utils.adebug_sleep(interval: float) None

Sleep for a given interval and log the sleep time at the logging.DEBUG level.

vcrtool.utils.audio_device_is_available(audio_device: str) bool

Check if an ALSA device can be used by attempting to open it with ffmpeg.

Return type:

bool

vcrtool.utils.debug_sleep(interval: float) None

Sleep for a given interval and log the sleep time at the logging.DEBUG level.

vcrtool.utils.debug_sp_run(*args: Any, **kwargs: Any) CompletedProcess[Any]

Run a subprocess and log the command at the logging.DEBUG level.

Return type:

sp.CompletedProcess[Any]

vcrtool.utils.get_pipewire_audio_device_node_id(name: str) tuple[str, str] | tuple[None, None]

Get the Pipewire node ID of an ALSA device.

Parameters:
name : str

The name of the audio device.

Returns:

The name and node ID of the audio device, or (None, None) if not found.

Return type:

tuple[str, str] | tuple[None, None]

Raises:

ValueError – If the ALSA device string is invalid.

vcrtool.utils.pad_right(value: T, list_: collections.abc.Sequence[T], max_length: int) list[T]

Pad a sequence to the right with a value.

Return type:

list[T]

Raises:

ValueError