@@ -476,3 +476,106 @@ and the full loop closes end-to-end — commissioned onto the Matter fabric, `Lo
476476Standalone setpoint writes (`OccupiedCooling/HeatingSetpoint`) return IM `0x86` (a cluster
477477deadband/limit constraint); the setpoint also rides the mode `0x65` at byte19. Build-by-build
478478v3→v4→v5 debugging detail is in git history.
479+
480+ ## 7. Cloud attribute table (`t_*` / `f_*` / `f_e_*`) [PROVEN location, 2026-07-19]
481+
482+ The module carries the full Hisense **cloud attribute** namespace as a name blob plus a
483+ lookup table. This is the vocabulary the ConnectLife app speaks, and it is the source of the
484+ app's "Self diagnostics" screen. Found by string+pointer analysis of `dumps/w41h1_dump1.bin`
485+ (local-only, gitignored); no bus tap required.
486+
487+ ### 7.1 Load base [PROVEN]
488+
489+ **`base = 0x9b6d0000`**, so `address = 0x9b6d0000 + file_offset`.
490+
491+ Derived by taking every attribute-name string offset, scanning the image for 32-bit LE words
492+ that could be pointers to them, and keeping the base with the most consistent hits:
493+ **77 of 77** names are referenced under this base and no other. Cross-checks against the
494+ addresses already in §5a hold: the `ac_8heat` getter `0x9b6f0ee6` maps to file `0x20ee6`,
495+ inside the code region.
496+
497+ This base was not previously written down. It is what makes any further static RE on this
498+ dump reproducible, so record it before anything else.
499+
500+ ### 7.2 Name blob and lookup table [PROVEN]
501+
502+ | item | file offset | address |
503+ |---|---|---|
504+ | attribute-name string blob | `0x13730a` .. `0x137700` | `0x9b80730a` .. |
505+ | attribute lookup table | starts `0x137e34` | `0x9b807e34` |
506+
507+ Table entries are **12 bytes**: `[meta0: u32 LE][meta1: u32 LE][name_ptr: u32 LE]`, where
508+ `name_ptr` points into the blob. Walking the table yields 66 entries before the name-pointer
509+ sanity check fails.
510+
511+ **The `meta0` encoding is NOT yet decoded.** See §7.5 before assuming anything about it.
512+
513+ ### 7.3 The fault namespace: what "Self diagnostics" reads [PROVEN names]
514+
515+ 27 `f_e_*` (fault/error) attributes exist. The app's four categories are a bucketing of these:
516+
517+ | app category | attributes |
518+ |---|---|
519+ | Sensors | `f_e_intemp` `f_e_incoiltemp` `f_e_outtemp` `f_e_outcoiltemp` `f_e_outgastemp` `f_e_tubetemp` `f_e_inhumidity` `f_e_wetsensor` `f_e_temp` |
520+ | Communications | `f_e_incom` `f_e_inwifi` `f_e_push` |
521+ | Motors | `f_e_infanmotor` `f_e_pump` `f_e_arkgrille` `f_e_upmachine` `f_e_dwmachine` |
522+ | Others | `f_e_ineeprom` `f_e_outeeprom` `f_e_inkeys` `f_e_indisplay` `f_e_inele` `f_e_invzero` `f_e_filterclean` `f_e_waterfull` `f_e_over_cold` `f_e_over_hot` `f_e_dwmachine` |
523+
524+ (The category mapping is inferred from the names, not read out of the firmware. The names
525+ themselves are `[PROVEN]`.)
526+
527+ These are almost certainly bit-packed into the 160-byte status frame we already receive once
528+ per second, which means **no new polling is needed to build a diagnostics feature**, only the
529+ bit map.
530+
531+ ### 7.4 Settable attributes worth noting [PROVEN names]
532+
533+ The `t_` prefix marks a settable attribute. Two matter for open issues:
534+
535+ * **`t_8heat`** and **`t_8c_heater_onoff`** both exist. 8 °C frost-guard heat therefore **is**
536+ commandable by the stock module. An earlier note in this session claimed no command verb
537+ existed for it; that was wrong, and the absence of an 8 °C control in the ConnectLife app is
538+ a UI choice, not a protocol limit.
539+ * `t_dimmer` exists alongside the display on/off control, which is the likely path to the
540+ panel-brightness *level* (the 2-bit `ac_power_display`), currently out of scope.
541+
542+ The table also names capabilities this unit probably does not implement: `t_fresh_air`,
543+ `f_co2_value`, `f_co2_level`, `t_onekey_selfclean`, `t_indoor_selfclean`,
544+ `t_outdoor_selfclean`, `t_hp_lock_onoff`, `t_ht_lock_onoff`, `t_heat_control_logic`,
545+ `t_interlock_onoff`, `t_demand_response`. Per the design rule in `docs/11 §5.1`, gate on the
546+ capability flags at runtime rather than deleting these paths.
547+
548+ ### 7.5 What is NOT established, and the trap in it
549+
550+ `meta0` is **not** a plain (byte, bit) frame position. The tempting read is that byte 2 is an
551+ offset and byte 3 a bit index, because the fault entries look orderly:
552+
553+ ```
554+ f_e_incoiltemp 00 00 18 0f f_e_inkeys 00 00 19 0f
555+ f_e_inhumidity 00 00 18 0e f_e_inwifi 00 00 19 0e
556+ f_e_infanmotor 00 00 18 0d f_e_inele 00 00 19 0d
557+ ```
558+
559+ Three things block that reading:
560+
561+ 1. **Duplicates.** `f_e_arkgrille` and `f_e_dwmachine` are both `00 00 18 0b`;
562+ `f_e_invzero` and `f_e_over_cold` are both `00 00 18 0a`. Two faults cannot share one bit.
563+ 2. **Non-uniform layout.** Entries such as `t_temp` (`03 03 03 0b`) and `t_fan_speed`
564+ (`08 01 0b 09`) use bytes 0 and 1, which every `f_e_*` entry leaves zero. So `meta0` is at
565+ least two different shapes, probably keyed by type.
566+ 3. **Anchor conflict.** `t_eco` is `00 00 8c 80` while `t_super` (turbo) is `14 05 15 0a`, yet
567+ eco and turbo are known to be **adjacent bits in status byte 35**. A position encoding that
568+ puts them far apart is refuted by hardware we already trust.
569+
570+ The duplicates most likely mean the walk crossed into a **second capability template**
571+ (`docs/11` documents multiple templates in flash), so the table bounds are themselves
572+ unconfirmed.
573+
574+ ### 7.6 Next step
575+
576+ Extend the §5a method to the fault attributes: disassemble the getters and read the
577+ `(status byte, bitmask)` pairs out of the code, exactly as was done for the `ac_*` capability
578+ flags. §5a already gives the pattern and the neighbouring addresses
579+ (`0x9b6f0d0a`, `0x9b6f0ee6`), and §7.1 now gives the base needed to navigate there.
580+
581+ Do **not** infer fault bit positions from `meta0` alone until §7.5 is resolved.
0 commit comments