NAME

Protocol::IR::Converter - Registry and manager for IR code protocols and formats

VERSION

version 0.08

SYNOPSIS

use Protocol::IR::Converter;
use Data::Dumper;

my $converter = Protocol::IR::Converter->new();

# Import a raw 32-bit NEC hex value
my $code = $converter->import_code('NEC', '0x10EF00FF');

# Tasmota IRSend JSON payload
print Dumper($code->to_irsend);
# { Protocol => 'NEC', Bits => 32, Data => '0x10EF00FF' }

# Export to Pronto Hex
my $pronto = $converter->export_code($code, 'Pronto');

# Decode Pronto Hex back into an Protocol::IR::Code
my $decoded = $converter->import_format('Pronto',
    '0000 006D 0022 0000 0157 00AC ...');

# Import an IRDB-style CSV file
my $codes = $converter->import_format('CSV',
    "functionname,protocol,device,subdevice,function\nKEY_POWER,NEC1,4,0,8");

# Export a set of Protocol::IR::Code objects to a HAIR WIG file
my $wig = $converter->export_codes('WIG', $codes,
    name  => 'Tigersecu DVR',
    brand => 'Tigersecu',
    kind  => 'dvr',
);

# Import and re-export Tasmota RawData captures
my $capture = $converter->import_format('Tasmota',
    "+9185-4490+650-500+655dE-1630C-505+630-525Ed...")->[0];
my $irsend = $converter->export_code($capture, 'Tasmota',
    style => 'comma', frequency => 38000);

DESCRIPTION

Protocol::IR::Converter is the heart of the Protocol::IR::Code distribution: a registry that knows which protocol and format modules are available and routes every import and export through them. All codes are normalized into Protocol::IR::Code objects, so a signal decoded from one format can be encoded into any other.

The following protocols and formats are registered by default:

  • Protocols: NEC (32-bit), NEC2 (32-bit, whole-frame repeat), 48-NEC1/48-NEC2 (48-bit), JVC (16-bit), JVC-48 (48-bit Kaseikyo), SAMSUNG (32-bit), SAMSUNG20 (20-bit), SAMSUNG36 (36-bit), NECX1/NECX2 (extended NEC, half header), MWM (Disney "Made With Magic", 24-144 bit serial)

  • Formats: Pronto, Tasmota, WIG, CSV, Mode2, LIRC

Pronto, Tasmota, Mode2, and LIRC are timing-based formats; Protocol::IR::Format::Pronto, Protocol::IR::Format::Tasmota, Protocol::IR::Format::Mode2, and Protocol::IR::Format::LIRC document their exact behavior. Protocol::IR::Format::WIG implements the HAIR "wireless infrared group" JSON format. Protocol::IR::Format::CSV imports IRDB-style button listings.

Decoding versus generating timings

For the timing-based formats (Tasmota RawData, Pronto Hex, and WIG), the goal of this library is to generate correct timings from fully decoded Protocol::IR::Code objects. That direction is authoritative: given a correctly decoded code, the emitted timings are exact.

The reverse direction -- decoding raw timings back into commands -- is best-effort. The protocol decoders match against simple header and mark/space thresholds and are not designed to handle the signal variance, jitter, and corruption that a real decoding library (such as IRremoteESP8266) accounts for. A capture may therefore fail to be recognized, or in rare ambiguous cases be misidentified. Conversions between fully decoded representations (raw hex, parameter hashes, IRDB CSV, WIG) are exact and reliable.

Conversions between the timing formats themselves (Tasmota RawData, Pronto Hex, and WIG) are exact and correct: a fully decoded code survives each format unchanged, and the timings generated for a given code are identical across all three (WIG carries Pronto hex, so Tasmota, Pronto, and WIG always agree). For recognized protocols, WIG files produced by HAIR are assumed to carry timings that have already been quantized and cleaned up, so they should decode correctly -- but, as with any raw timing input, this is not guaranteed.

INSTALLATION

perl Makefile.PL
make
make test
make install

Or with a cpan client:

cpanm Protocol::IR::Code

The modules have no runtime dependencies beyond core Perl. JSON::PP (core since Perl 5.14) is declared as a prerequisite for older versions.

Two command-line tools are installed with the distribution:

  • ir-irdb2wig -- convert an IRDB CSV file (local, fetched from a URL, or downloaded from the IRDB repository by device path) to a HAIR WIG JSON file.

  • ir-convert -- general converter between the supported formats (CSV, WIG, Pronto, Tasmota, LIRC).

Both read the Protocol::IR::Code modules from your normal Perl installation, so they work from any directory once the distribution is installed.

METHODS

new

my $converter = Protocol::IR::Converter->new;

Creates a converter, registering the bundled protocols (NEC, NEC2, 48-NEC1, 48-NEC2, JVC, JVC-48, SAMSUNG, SAMSUNG20, SAMSUNG36, NECX1, NECX2, MWM) and formats (Pronto, CSV, WIG, Tasmota, Mode2, LIRC).

import_code

my $code = $converter->import_code('NEC', '0x10EF00FF');
my $code = $converter->import_code('JVC', { address => 3, command => 12 });

Builds an Protocol::IR::Code for the named protocol from either a raw value (string or number) or a hash of parameters. Dies on an unregistered protocol.

export_code

my $pronto = $converter->export_code($code, 'Pronto');
my $irsend = $converter->export_code($code, 'Tasmota',
    style => 'comma', frequency => 38000);

Serializes a single Protocol::IR::Code object into the named format, passing any extra options through to the format's export method.

export_codes

my $wig = $converter->export_codes('WIG', \@codes, name => 'Remote');

Serializes one or more Protocol::IR::Code objects into a container format such as WIG. A single object is wrapped in an arrayref automatically.

import_format

my $codes = $converter->import_format('WIG', 'remote.wig.json');
my $code  = $converter->import_format('Pronto', $pronto_str);

Parses external input in the named format (file path, raw string, JSON, etc.) into a list of Protocol::IR::Code objects. Returns an arrayref. Dies on an unregistered format.

register_protocol

$converter->register_protocol('RC5', 'Protocol::IR::RC5');

Registers a protocol handler class under a name. See "EXTENDING THE FRAMEWORK".

register_format

$converter->register_format('JSON', 'Protocol::IR::Format::JSON');

Registers a format handler class under a name. See "EXTENDING THE FRAMEWORK".

get_protocol

my $class = $converter->get_protocol('NEC');

Returns the handler class registered for a protocol name, or undef.

get_protocols

my @classes = $converter->get_protocols;

Returns the registered protocol handler classes in deterministic registration order. Pronto and Tasmota decoding try protocols in this order, so handlers whose timing signatures overlap must be registered most-specific-first.

cross_protocol

my $equivs = $converter->cross_protocol($code);

Converts a code to its equivalent in a cross-protocol partner. Currently supports:

SAMSUNG <-> NECX2

Both protocols share identical 4500/4500 us half-header timing and 32-bit LSB-first encoding, but name the fields differently. The Samsung address is the bit-reversal of the NECX2 device byte, and likewise for the command/function byte. See "CROSS-PROTOCOL MAPPING" in Protocol::IR::Proto::SAMSUNG for details.

Returns an arrayref of Protocol::IR::Code objects (zero or one entries) in the equivalent protocol. Returns [] when the protocol has no cross-protocol partner.

Example:

# A Samsung TV POWER capture (address=0xE0, command=0x40)
# converts to NECX2 (device=7, subdevice=7, function=2)
my $necx2_equivs = $converter->cross_protocol($samsung_code);
my $necx2_code   = $necx2_equivs->[0];

# A NECX2 code from an IRDB CSV converts to SAMSUNG for capture matching
my $sam_equivs = $converter->cross_protocol($necx2_code);

EXTENDING THE FRAMEWORK

Adding a new protocol (Protocol::IR::NAME)

Create a package under lib/Protocol/IR/ implementing these methods:

  • decode_raw($class, $raw_int) -- takes a raw packed integer/hex value and returns an Protocol::IR::Code object.

  • decode_params($class, %args) -- accepts discrete parameters (device, subdevice, command) and returns an Protocol::IR::Code object.

  • decode_timing($class, \@burst_pairs_us) -- accepts an arrayref of microsecond [mark_us, space_us] pairs, matches the protocol's timing signature (headers, mark/space thresholds), and returns an Protocol::IR::Code object if valid, or undef if non-matching.

  • to_pronto($class, $ir_code) -- converts an Protocol::IR::Code object into a Pronto Hex string.

Register it in Protocol::IR::Converter::new():

$self->register_protocol('RC5', 'Protocol::IR::RC5');

Adding a new format (Protocol::IR::Format::NAME)

Create a package under lib/Protocol/IR/Format/ implementing these methods:

  • export($class, $ir_code, $registry) -- serializes one or more Protocol::IR::Code objects into the target format.

  • decode($class, $input_data, $registry) -- parses external input (file path, raw string, JSON, etc.) into one or more Protocol::IR::Code objects, using $registry->import_code(...) to build them.

Register it in Protocol::IR::Converter::new():

$self->register_format('JSON', 'Protocol::IR::Format::JSON');

SUPPORT

Source code: https://github.com/bwarden/perl-protocol-ir

Bug reports and feature requests: https://github.com/bwarden/perl-protocol-ir/issues

AUTHOR

Brett T. Warden <bwarden@cpan.org>

COPYRIGHT AND LICENSE

Copyright (c) 2026 Brett T. Warden

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation.