Add Panasonic CW-HU70ZA window A/C (Broadlink, code 1035)#1559
Open
sam0737 wants to merge 1 commit into
Open
Conversation
e598fd8 to
db53192
Compare
Window-type unit (remote ACXA75C20160). Modes auto/cool/dry/heat, fan auto/low/mediumLow/mid/mediumHigh/high, temp 16-30C. nanoeX is folded into the swing dropdown (auto/fixed x nanoeX on/off) since SmartIR has no extra toggle axis. Codes generated from the decoded Panasonic IR protocol and verified to match captured frames. Co-authored-by: Cursor <cursoragent@cursor.com>
db53192 to
f7ed601
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add Panasonic CW-HU70ZA window A/C (Broadlink, code
1035)Adds a SmartIR climate code for a Panasonic window-type air-conditioner (Hong Kong, ~2023; remote
ACXA75C20160). The codes are generated from a reverse-engineered protocol spec, not from raw recordings, and every generated frame is round-trip verified against an independent decoder.Highlights
operationModes: auto / cool / dry / heatfanModes: auto / low / mediumLow / medium / mediumHigh / highswingModes:auto,fixed, plusauto_nanoex/fixed_nanoex— the unit's nanoeX feature is folded into the swing dropdown because SmartIR's climate schema has no slot for an extra toggle.precision: 0.5). The protocol carries this in byte 14 (= °C × 2), so the half-degree maps to byte 14 bit 0.CW-HU70ZA(my unit; remoteACXA75C20160).CW-HU90ZA,CW-HU120ZA,CW-HU180ZA,CW-HU70AA,CW-HU90AA,CW-HU120AA,CW-HU180AA,CW-HZ70AA,CW-HZ90AA,CW-HZ180AA.CW-HU*= cooling-only;CW-HZ*= heat pump (also uses theheatmode). TheZAsuffix is the ~2023 generation,AAis the current "Inverter PRO Wi-Fi" refresh. Cooling-only units ignoreheat; non-nanoeX units ignore the nanoeX swing options.script/buttonentities (see attachment below).This unit is the IRremoteESP8266 Panasonic A/C protocol (same
02 20 E0 04signature, 27-byte LSB-first state,sum(state[8..25]) & 0xFFchecksum), with two model-specific quirks: nanoeX lives in byte 25 bit0x04(the library doesn't model it), and Quiet/Powerful are short toggle frames rather than bits in byte 21.Please read the protocol write-up below for the full details (physical layer, frame layout, checksum, every field, the fixed magic bytes, the short toggle frame, and a comparison to IRremoteESP8266). The generator script and the Quiet/Powerful YAML are attached for reference / reproducibility.
📄 panasonic-ac-ir-protocol.md — full protocol write-up (start here)
Panasonic Window A/C — Infra‑Red Remote Protocol
A complete, from‑scratch description of the IR protocol used by my Panasonic
window air‑conditioner, reverse‑engineered from Broadlink RF/IR captures. It is
written so that a reader with no prior knowledge of Panasonic A/C remotes
can reconstruct a valid signal byte‑for‑byte: carrier, mark/space timing, frame
layout, repetition, checksum, every field, the fixed "magic" bytes, and the
separate short frame used by the Quiet and Powerful buttons.
1. Applicable models
The air‑conditioner itself is a Panasonic window‑type unit (Hong Kong,
~2023). I think it is a
CW-HU70ZA, but that is from memory — the appliancemodel number is not printed on the unit's outer shell where I looked, and it
is not encoded in the IR signal either, so I can't confirm it from the
capture.
What I can read off the hardware is the remote control part number:
ACXA75C20160(see §1.1 for what that means). Note the remote part number islikewise not present in the IR frame — the frame only carries Panasonic's
generic model bytes (§7/§9), so the remote number and the appliance number
cannot be derived from each other or from the bytes.
The frame carries Panasonic's generic A/C signature (
02 20 E0 04 …, see§7), which is shared across essentially the whole Panasonic A/C line. In
practice this same protocol — possibly minus features such as nanoeX on
cooling‑only units — should drive the current Hong Kong window range, e.g. the
models listed at
Panasonic HK — Window Air‑Conditioner.
Units without nanoeX simply ignore the nanoeX bit (§6.6); cooling‑only units
ignore the heat mode value.
1.1 Remote part number
ACXA75C20160This is a standard Panasonic remote‑control spare‑part number, not an encoding
of any AC capability. It breaks down as:
ACXA75CA75Cseries).20160The same remote is commonly written three ways: full part number
ACXA75C20160,short
A75C20160, or just the20160that is usually printed on the back ofthe remote. (Older units sometimes use a
CWA75C…molding number for the samething.) To find a replacement you match this exact number, because each one
is tied to a specific group of AC models and a proprietary IR code set
(example listings).
I could not find a public listing for
ACXA75C20160specifically, but itsformat is genuine and its
201xxrange sits alongside other recent (2020s)remotes such as
ACXA75C21620/ACXA75C21700, which is consistent with a~2023 unit. The trailing digits are roughly sequential by release, not a feature
code — so they tell you which remote, not what the protocol does.
2. Physical layer
2.1 Timing (measured, with canonical Panasonic values)
All marks have the same length; the following space distinguishes a
0from a
1.0space (after mark)1space (after mark)Decision rule for a receiver: after each ~440 µs mark, measure the space —
space ≳ 850 µs →
1, else0(a threshold halfway between 439 and 1278 µsis robust).
3. Frame structure & repetition
One button press transmits one message made of two sections (frames):
space can be measured; the section/message gap follows.
state.
receiver acts on a single well‑formed message. Re‑sending the identical
message is idempotent for absolute fields (mode/temp/fan/swing) because they
are absolute, not relative.
The Quiet/Powerful buttons are the exception — they send a much shorter
16‑byte message (§8).
4. Bit / byte encoding
Bits are packed LSB‑first: the first bit received is bit 0 of the byte, the
eighth bit is bit 7.
Example: spaces decoded as
0 1 0 0 0 0 0 0→ byte0x02.5. Checksum
The last byte of Frame 2 is a checksum:
For the full 27‑byte state that is
sum(state[8 .. 25]) & 0xFF == state[26](bytes 8–25 are exactly the 18 data bytes of Frame 2 before the checksum).
The short Quiet/Powerful frame uses the same rule over its own shorter
Frame 2:
sum(state[8 .. 14]) & 0xFF == state[15](§8).Frame 1 has no checksum (it is a fixed preamble, §7).
6. Full state frame (27 bytes)
Byte map below was confirmed across 723 captured full frames. "CONST" bytes
are identical in every capture (the reproducible "magic"); "FIELD" bytes carry
settings.
0220E004000000060220E004000x80marks the short toggle frame, §8800D0x0D= its "SwingH Auto" and never changes (§6.5/§9)000EE0000081000x04= nanoeX (§6.6)A known‑good baseline (Auto, 16 °C, fan Auto, swing Auto, nanoeX on):
6.1 Power — byte 13, bit 0
10"Off" is sent as a normal full frame with bit 0 cleared (e.g. byte 13 =
0x00).6.2 Mode — byte 13, high nibble (bits 4–7)
0234So byte 13 =
(modeNibble << 4) | powerBit. Observed values:0x00(off/auto),0x01(auto on),0x21(dry on),0x31(cool on),0x41(heat on).6.3 Temperature — byte 14
Range 16–30 °C → byte 14 =
0x20…0x3C. In every captured frame theremote sent whole degrees, so byte 14 was always even (bit 0 = 0).
But because byte 14 is
°C × 2, bit 0 is a 0.5 °C step, so half-degrees arerepresentable: e.g.
24.5 °C → round(24.5 × 2) = 0x31. Whether a given unitactually acts on the half-degree bit is untested — the generated SmartIR
file sets
precision: 0.5to try it.6.4 Fan speed — byte 16, high nibble (bits 4–7)
A34567(Other nibble values are not produced by this remote.)
6.5 Swing — byte 16, low nibble (bits 0–3)
On my unit (a window A/C) this controls the horizontal louver — there is
no separate vertical‑swing feature. The values observed are:
F5byte 16 =
(fanNibble << 4) | swingNibble, e.g.0xAF= fan Auto + swing Auto;0x35= fan Low + swing Fixed.Comparison with IRremoteESP8266 — and why the axis label differs. In
IRremoteESP8266 this exact nibble (byte 16 low) is vertical swing
(
getSwingVertical/setSwingVertical), while horizontal swing lives inbyte 17 low nibble (
getSwingHorizontal). So the position you control is theone the library labels vertical, not horizontal. The names are a
split‑wall‑unit convention (airflow tilted up/down vs panned left/right); a
window unit has a single louver, so what you see as "horizontal" sits in the
byte‑16 slot the library calls "vertical." Mapping the values:
FkPanasonicAcSwingVAuto(0xF) — exact match5kPanasonicAcSwingVLowest(0x5) — i.e. vane parked at the lowest detentD(constant)kPanasonicAcSwingHAuto(0xD) — "horizontal swing = auto", left untouchedSo your "fixed" position is, in the library's vocabulary, the lowest vertical
detent (
0x5), and your byte 17 is pinned to the library's horizontal‑autovalue (
0xD) because your remote never drives that second axis. OtherIRremoteESP8266 vertical values you won't see from this remote: Highest
0x1,High
0x2, Middle0x3, Low0x4.6.6 nanoeX — byte 25, mask
0x040x040x06)0x02)Confirmed by holding everything else constant and toggling only nanoeX: the
only changed bit (besides the checksum) was byte 25
0x04. The0x02bit isa constant base value for this byte. Cooling‑only / non‑nanoeX units ignore it.
7. Fixed "magic" bytes (required to reproduce)
These never change and must be emitted verbatim or the unit ignores the
frame:
02 20 E0 04is the Panasonic A/C manufacturer/protocol signature andappears at the start of both frames. Bytes 19–20 (
0E E0) encode the"timer disabled" special value, and byte 23 (
81) is the model signature.8. Short frame — Quiet / Powerful toggle
The Quiet and Powerful buttons do not send the full state. They send
a dedicated 16‑byte message: the normal 8‑byte Frame 1, then a shortened
8‑byte Frame 2. Captured bytes:
02 20 E0 0402 20 E0 0480800x80; full frame has0x00)818633353A41sum(bytes 8–14) & 0xFFBoth verified:
sum(02+20+E0+04+80+81+33)&0xFF = 0x3A✓ andsum(02+20+E0+04+80+86+35)&0xFF = 0x41✓ — i.e. the same checksum rule as thefull frame, just over the shorter body.
Why these behave as pure toggles. The frame is physically too short to
contain the mode/temperature/fan/swing/nanoeX fields (those live at bytes
13/14/16/25 of the 19‑byte Frame 2, which is absent here). So the message says
only "toggle Quiet" / "toggle Powerful" and the unit keeps whatever it was
already running. This matches the observed behaviour: pressing Quiet/Powerful
changes nothing else. It is not that the A/C "ignores" other fields when a
bit is set — there simply are no other fields in this frame.
To reproduce, replay these two captures verbatim; do not synthesise them
from a full‑state encoder.
9. Comparison with IRremoteESP8266
IRremoteESP8266
(
ir_Panasonic.cpp/.h) is the de‑facto reference for Panasonic A/C IR. Ourunit is that protocol — same carrier, same timings, same 27‑byte
LSB‑first state, same checksum (
sum(state[8..25]) & 0xFF), same02 20 E0 04signature and the same
kPanasonicAcSection1 = {02,20,E0,04,00,00,00,06}preamble. Differences are in a few model bytes and in how features are sent.
9.1 Magic / signature bytes
02 20 E0 04 00 00 00 0602 20 E0 0480F/5getSwingVertical)F=SwingVAuto,5=SwingVLowest (§6.5)0DgetSwingHorizontal)0x0D=kPanasonicAcSwingHAuto; pinned, never driven (window unit has one louver, §6.5)0E E00x600)000081& 0x80set ⇒ JKE‑family02/060x06is the DKE reset constant0x04as nanoeX9.2 Closest model
By signature bytes our unit is a hybrid:
0x81(the0x80bit) is IRremoteESP8266's JKE marker.Its JKE detection additionally expects byte 17 ==
0x00, but ours is0x0D, so the library would classify it as unknown while still decodingevery standard field correctly.
0x06matches the library's DKE reset constant(
stateReset()setsremote_state[25] = 0x06forkPanasonicDke).Closest enumerated model: JKE (with a DKE‑like feature byte). For sending,
treat it as generic Panasonic A/C and preserve our exact constant bytes rather
than relying on a specific model preset.
9.3 Feature handling differs (important)
a fixed model byte (
0x00, or0x06for DKE) and never toggles it. Ourempirical finding — byte 25 bit
0x04= nanoeX — is beyond what thelibrary models. A library‑generated frame would not carry/toggle nanoeX.
27‑byte frame —
kPanasonicAcQuietOffset = 0andkPanasonicAcPowerfulOffset = 5, both in byte 21 (mutually exclusive).Our remote instead sends the short 16‑byte toggle frame (§8) and leaves
byte 21 =
0x00in every full frame. Consequence: driving the unit through anIRremoteESP8266‑style encoder would re‑transmit the whole state (re‑asserting
mode/temp/fan/swing) to flip Quiet/Powerful — the opposite of our remote's
minimal toggle.
10. End‑to‑end reconstruction recipe
To build, say, Cool, 24 °C, fan High, swing Auto, nanoeX on:
02 20 E0 04 00 00 00 06.02 20 E0 04 003) + power on (1) →0x310x30(a half-degree such as 24.5 °C →round(24.5 × 2)=0x31)807) + swing Auto (F) →0x7F0D 00 0E E0 00 00 81 000x02+ nanoeX0x04→0x06sum(bytes 8..25) & 0xFF.mark → 10 ms gap → header → Frame 2 bits → trailing bit mark → 100 ms
gap. Each bit = ~432 µs mark + (432 µs for
0/ 1296 µs for1) space.For Quiet/Powerful, skip all of this and replay the captured 16‑byte frame
from §8 verbatim.
11. Broadlink capture notes (for replay tooling)
The raw captures are Broadlink IR packets, Base64‑encoded:
0x26(IR), byte 1 = repeat count, bytes 2–3 = payload length(little‑endian).
µs = ticks × 8192 / 269(1 tick ≈ 30.46 µs).0x00followed by a big‑endianuint16 (note: the length header is little‑endian, but these extension values
are big‑endian — easy to get wrong, and it only affects the long gap values,
not the bit timings).
0x0D 0x05trailer / padding.Demodulation: strip the wrapper, split on the ~10 ms section gap into Frame 1 /
Frame 2, take every (mark, space) pair, map space>threshold→
1, pack LSB‑first.⚙️ generate-smartir.mjs — generates this
1035.jsonfrom the spec (with self-verification)🔇 panasonic-quiet-powerful.yaml — standalone Quiet/Powerful toggle entities