07 · Stock-parity gap matrix
Single source of truth for “what does the stock A/C do that we don’t (yet) mirror.” Consolidates
the previously-scattered lists in 05-ha-control-and-native-ui.md, 01-expose-all-clusters.md,
reverse-engineering/docs/{03,05,10}, and the recon HIL-RUNBOOK.md.
Columns: DEC = decoded in the driver · CTRL = a command builder exists · EXP = exposed
to Matter/HA. Capability source = the 0x66/40 ProductType HisenseFeatures flags
(hisense_rs485.h:338-374, decoder .cpp:247-282); the Ghidra-authoritative bit map is
reverse-engineering/docs/10 §5a (handle_producttype_cmd_result @0x9b6f0c4c).
Matrix
| Stock feature | DEC | CTRL | EXP | Notes |
|---|---|---|---|---|
| Power on/off | ✅ | ✅ | ✅ | OnOff ep1 |
| Mode (cool/heat/dry/fan/auto) | ✅ | ✅ | ✅ | Thermostat FeatureMap 35 (Heat+Cool+Auto); dry/fan via HA unlock |
| Setpoint | ✅ | ✅ | ✅ | Occupied Cooling/Heating Setpoint |
| Fan (6 discrete speeds) | ✅ | ✅ | ✅ | FanControl mode + percent |
| Eco / power-save | ✅ | ✅ | ✅ | ep3 OnOff “Eco” + mfg 0xFFF1FC00/0x0000 |
| Turbo / boost | ✅ | ✅ | ✅ | ep5 OnOff “Turbo” + mfg /0x0002 |
| Mute / quiet | ✅ | ✅ | ✅ | ep4 OnOff “Quiet” + mfg /0x0002; hisense_build_mute_frame, fixed 2026-08-19 (see the note below). |
| Sleep profile (4) | ✅ | ✅ | ✅ | ep6 ModeSelect; hisense_build_sleep_frame, fixed 2026-08-19. All four profiles verified. |
| Aux/PTC heat relay | ✅ | – | ✅ | ep7 BooleanState (read-only status) |
| Outdoor + coil temp | ✅ | – | ✅ | ep2 / ep8 TemperatureMeasurement |
| Power (V/I/W) | ✅ | – | ✅ | ElectricalPowerMeasurement (see #16, power_estimate.h) |
| Vertical swing on/off | ✅ | ✅ | ✅ | FanControl RockSetting on ep1, both builds. Shipped under #19. |
| Display / panel on/off | cap only | ✅ | ✅ | OnOff switch on ep9 (write-only, the A/C reports no display state back). Shipped under #19/#33. (Dimmer level = ac_power_display, still needs new RE.) |
| 8 °C frost-guard heat | ✅ cap | ❌ | ✅ ro | Capability bit ac_8heat (byte26 0x80) read as heat_8c; ac_enable_8heat (byte39 0x04) read as enable_8heat when ext_valid. No control frame RE’d. docs/05:79 marks it likely absent on this unit. |
| Purify / ionizer | ✅ cap | ❌ | ✅ ro | ac_purify (byte23 0x08) read as purify; live purify_on (b36 0x20) “bit always 0, feature absent on this unit”. No builder. |
| AI / smart | ✅ cap | ❌ | ✅ ro | ai (byte28 0x40). Capability only, no control frame. |
| Demand-response | ✅ cap | ❌ | ✅ ro | demand_resp 2-bit (byte35). Capability only. |
| Infinite / stepless fan | ✅ cap | ⚠️ | ✅ ro | infinite_fan (byte25 0x08); we drive 6 discrete speeds only, not stepless. |
| 8-position louvre aim | ✅ cap | ⚠️ | ✅ ro | swing_dir_8/swing_follow capability + on/off swing decoded, but no per-position index command. |
| Fresh-air / dew | – | – | – | No ac_* flag exists: not in the stock feature set, nothing to mirror. |
✅ roin EXP = the capability flag reads read-only in HA through the Capabilities sensor (Features1, #82/#39). No control frame is RE’d for any of these, so none is drivable yet.
Cheap wins: shipped
The two formerly-flagged app-layer gaps (vertical swing → FanControl RockSetting on ep1; display
on/off → an OnOff switch on ep9) both shipped under #19, on both AmebaZ2 and ESP32.
Everything else (8 °C heat, purify, dimmer level, AI, demand-response, stepless fan, 8-pos louvre) is
now read-only exposed through the Features1 capability bitmap (#82, #39): each still needs its
control frame reverse-engineered before it can be driven, and several are argued
physically-absent on this unit (docs/05:79-80). Not cheap; defer / track under #52.
Reading capabilities live
The HisenseFeatures set (hisense_get_features) is written to the ep1 mfg cluster 0xFFF1FC00 as
attribute 0x0012 (Features1, packed bitmap), alongside CompressorHz (0x0010) and Faults1
(0x0013). matter-server reads all three live, and the hisense-unified-ac HACS integration decodes
them into the Compressor frequency, Capabilities, and Faults entities on nodes 14/35/62 (#82, #39,
closed). The telnet :2323 decode path still works as a secondary read.
Data-quality bugs found during this review (→ issue #83): RESOLVED
Two field-naming bugs, fixed 2026-07-16/18, both labels only (not used for control), so neither changed behaviour:
HisenseFeatures.purify/.q_displaywere misnamed; renamed toheat_8c(byte26 0x80 =ac_8heat) andpurify(byte23 0x08 =ac_purify) per the Ghidra decode (docs/10 §5a). The byte reads were always correct.ac_q_display(byte39 0x40),ac_enable_8heat(byte39 0x04),ac_trans_102_64(byte38 0x08) were undecoded. Added asq_display/enable_8heat/trans_102_64. They live on payload[0x19]/[0x1A], which stock only sends when the reply is long enough (frame len > 39); gated byHisenseFeatures.ext_valid, so a0withext_valid == falsemeans “unknown,” not “absent”.
Verified on hardware 2026-07-18 (node 28, fw 1.0.10, features on the :2323 console): the
0x66/40 reply is 45 B, so the extended tier is reachable, and ac_q_display reads 1 on this unit,
a capability invisible while the field name held ac_purify. ac_enable_8heat and
ac_trans_102_64 read 0. Base-tier flags match the 2026-07-16 capture unchanged (RE docs/11
§5.1). Consumers updated: matter_drivers.cpp on_features logging and the esp32
diag_console features command, both now print the extended tier or explicitly say UNKNOWN.
Mute and sleep: one missing byte (2026-08-19, FIXED)
Both were broken on all three firmwares and are now fixed in the shared driver, with no change at any call site.
hisense_build_mute_frame / hisense_build_sleep_frame build from a zeroed buffer: header,
f[23]=0x04, and the one named byte. Every COMBINED command also writes frame[31] = 0x01.
Every control riding the combined frame worked; both single-field frames were accepted on the
wire and silently ignored. Adding that marker to hisense_build_single_field() fixed both.
Verified on a live CF35LR03G: all four sleep profiles select (sleep_raw 2/4/6/8, and 0 for
off), the Sleep select entity reads every option back, and mute engages with fan_raw 0x02.
What made it hard to see is worth remembering. From sleep_raw = 0, “selected a profile” and
“ignored” are the same observation, and byte 17 in the combined frame does act, but only to
cancel. Every probe from a zero start therefore looked like silence. Setting a profile on the
A/C’s own remote first, then commanding a different one, is what separated the two cases.
Both Matter builds call the same two builders (matter_drivers.cpp 830/855,
app_main.cpp 237/243), so their ep4 Quiet and ep6 Sleep are fixed by this driver change.
Neither image has been rebuilt to confirm it on hardware yet.