Skip to content

Add Sharp AY-XP30EJ climate codes (device 1302)#1583

Open
mmank wants to merge 2 commits into
smartHomeHub:masterfrom
mmank:add-sharp-ay-xp30ej
Open

Add Sharp AY-XP30EJ climate codes (device 1302)#1583
mmank wants to merge 2 commits into
smartHomeHub:masterfrom
mmank:add-sharp-ay-xp30ej

Conversation

@mmank

@mmank mmank commented Jul 22, 2026

Copy link
Copy Markdown

Summary

Adds IR codes for the Sharp AY-XP30EJ / AE-X30EJ (Eco Inverter, Plasmacluster) split AC, captured directly from the physical remote via a Broadlink RM4 mini.

  • Modes: Cool, Heat (18-32°C, 1° steps), Dry
  • Fan speeds: Auto, Soft, Low, High (Dry is fixed at Auto per the unit's own manual, so it's not independently controllable there)
  • 181 total codes

Why this needed more than the usual "point remote, click Learn" process

This model has a dedicated "power on" pulse type, distinct from ordinary temperature/mode "set" pulses. Sent to an already-off unit, a plain "set" pulse (which is what naive learning captures) is silently ignored — the AC only starts from a "power on"-type packet. Every code in this file was captured (or generated, see below) as that correct packet type, and verified end-to-end through Home Assistant's climate entity, starting from a genuine off state, for both Cool and Heat.

Checksum reverse-engineering (for the fan-speed matrix)

Manually capturing all 120 Cool/Heat × temp × fan combinations wasn't practical, so I reverse-engineered the packet's checksum byte instead of capturing every combination by hand.

Byte layout (13 bytes, LSB-first per byte, after the sync header):

Byte Meaning
0-3 Constant aa 5a cf 10
4 Temperature - 17 (0x00 for modes without absolute temp)
5 Action type: 0x1_=ON, 0x2_=OFF, 0x3_=Set (ignored while off), 0x6_/0x7_=Turbo
6 Fan (hi nibble: Auto=2/Soft=3/Low=5/High=7) + Mode (lo nibble: Auto=0/Heat=1/Cool=2/Dry=3)
7-11 Mostly constant, minor swing/turbo-related flags
12 Checksum

The checksum turned out to be the standard Sharp A/C checksum already documented in IRremoteESP8266 (ir_Sharp.cpp, calcChecksum): XOR all 12 payload bytes together, XOR in the low nibble of byte 12 (which is payload, constant 1 on this remote — not part of the checksum), fold the high nibble into the low one, and store the resulting nibble in the high nibble of byte 12.

I initially solved it blind as a set of GF(2) linear systems fit over ~70 captured samples (which is what made it clear the function was pure XOR — that fit is what the earlier revision of this description showed), then recognized the 13-byte frame as the known Sharp protocol and confirmed the canonical algorithm matches 73/73 captured samples, including swing-button captures the blind fit had missed.

def sharp_checksum_byte(payload: list[int], low_nibble: int = 1) -> int:
    """payload: the 12 payload bytes. Returns the full 13th byte."""
    x = 0
    for b in payload:
        x ^= b
    x ^= low_nibble
    x ^= x >> 4
    return ((x & 0xF) << 4) | low_nibble

All 122 unique codes in this file verify against this algorithm, and generated codes were additionally spot-checked directly against the physical AC on combinations that were never captured (Cool 27°C/High fan, Heat 22°C/Soft fan, Cool 20°C/Low fan via the actual HA climate entity, all starting from an off state) — all correct.

Testing

  • Verified via a real climate entity (platform: smartir, device_code: 1302) against the physical unit
  • Off → Cool and Off → Heat transitions confirmed (compressor engages, correct temperature behavior)
  • Multiple fan speeds spot-checked audibly against the real unit

Limitations

  • Dry mode's fan speed is fixed at Auto (matches the unit's own remote — Dry doesn't allow fan speed selection)
  • Ion/Plasmacluster and swing/louvre control aren't included (out of scope for climate control)

mmank added 2 commits July 22, 2026 23:18
IR codes captured directly from the RM4 mini via the physical remote,
reverse-engineering the protocol to confirm each code carries the
dedicated "power on" pulse type this model requires to start correctly
from an off state (plain temp/mode "set" pulses are ignored while off).

Covers Cool 18-32C and Heat 18-32C (1C steps) plus Dry. Fan speed is
fixed at Auto (not independently controlled) and swing/Plasmacluster-ion
are not included -- verified working for the covered modes on real
hardware, including the off -> on transition for both Cool and Heat.
Reverse-engineered the checksum algorithm (a linear/XOR function over the
payload bits, solved via GF(2) linear regression against ~70 captured
samples, cross-validated at ~96% full-byte accuracy) to generate the
fan-speed x temperature matrix computationally rather than requiring
120 additional manual captures.

Every generated code was built by patching only the temp/mode/fan fields
of a real validated capture and recomputing the checksum, keeping all
other protocol framing identical to genuine hardware-captured signals.
Spot-checked multiple never-before-captured combinations directly against
the physical AC (Cool 27C/High fan, Heat 22C/Soft fan, Cool 20C/Low fan
via the actual HA climate entity end-to-end) -- all confirmed correct.
@mmank

mmank commented Jul 22, 2026

Copy link
Copy Markdown
Author

Did AI write most of it, including the PR description? Certainly. Did I test it? Yes, it works. The remote I have is "CRMC-A669JBEZ". Generated chat summary:

For anyone curious about the process (or debugging a similar Sharp unit):

Started with the obvious path. Set up the RM4 mini in HA, then tried the two existing Sharp code sets (1300, 1301) already in this repo, hoping one would just match. Neither did cleanly — 1300 got close enough to trigger real cooling at one test temperature, which was a useful signal (confirmed a related protocol family) but the mode/temp mapping wasn't reliable across the board.

Learned real codes from the physical remote instead. Used remote.learn_command to capture Off + Cool 18-32°C + Heat 18-32°C + Dry directly. Wired them into a climate entity via device_code. Cool 24°C worked... but only when the AC was already running. From a genuine off state, nothing happened except the fan blowing (no compressor). Fan speed changes did not
hing at all.

Found the actual manual (Sharp AY-XP30EJ / AE-X30EJ, a model neither 1300 nor 1301 covers) and noticed something specific to this remote's UX: separate physical/logical handling for
"start operation" vs "adjust settings while running."

Bypassed Home Assistant and captured raw IR directly with python-broadlink, dumping pulse timings for individual button presses (power on/off, each temp step, each mode, each fan s
peed) and decoding them by hand: constant-length mark + variable-length space per bit (pulse-distance encoding), 13 bytes, LSB-first per byte.

That gave a full byte-level map:

Byte Meaning
0-3 constant header aa 5a cf 10
4 temperature − 17
5 action type0x1_=Power ON, 0x2_=Power OFF, 0x3_=Set, 0x6_/0x7_=Turbo
6 fan (hi nibble) + mode (lo nibble)
7-11 mostly constant, minor swing/turbo flags
12 checksum

The bug, explained: this AC has a real dedicated "power on" pulse, distinct from ordinary "set" pulses. A 0x31 (Set) packet is only obeyed while the unit is already running — sent
to an off unit, it's silently ignored. Every code learned via the normal remote.learn_command flow was a Set-type packet (since the remote was just being dialed, not power-cycled), so
the whole "off → some state" path was broken by design, not by a bad capture.

Relearned everything as the correct 0x11 (ON) packet type, using a "remote shows OFF → dial mode/temp → press Power" procedure, validating each capture's decoded bytes in real time
against what was intended. That fixed Off→Cool and Off→Heat completely.

Fan speed was the next wall: doing this properly for 4 fan speeds × 15 temps × 2 modes meant 120 more manual captures. Instead, reverse-engineered the checksum byte — turned out to be a linear (XOR) function over GF(2) of the other 96 payload bits. Solved it by capturing ~70 samples across the real parameter space and solving 8 independent linear systems (one pe
r checksum bit) via Gaussian elimination over GF(2). Cross-validated at ~96% full-byte accuracy on held-out samples (the handful of misses all traced back to one under-sampled field — the swing/louvre byte — that this file never actually uses), then spot-verified several never-before-captured combinations directly against the real hardware. All correct.

Generated the full 181-code matrix computationally from there, redeployed, and re-verified end-to-end through the actual HA climate entity (not just the raw IR layer) for several combinations, starting from a genuine off state each time.

@mmank

mmank commented Jul 23, 2026

Copy link
Copy Markdown
Author

... And a newer model figured out the checksum algorithm instantly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant