Skip to content

0.3.0 Track A: attribute cache + publish_* wired#33

Merged
avrabe merged 1 commit into
mainfrom
0.3.0/attribute-cache
May 28, 2026
Merged

0.3.0 Track A: attribute cache + publish_* wired#33
avrabe merged 1 commit into
mainfrom
0.3.0/attribute-cache

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 27, 2026

Summary

RsMatterBridge::publish_reading and publish_alert no longer call unimplemented!(). They now translate the wohl event to the right Matter cluster + attribute (via the cluster mapping table from PR #27 + B1 fix), apply the unit conversion (per DESIGN.md §7.4), and write the typed value into a thread-safe in-memory cache.

This is the push side of the push/pull mediation the architecture critic flagged in PR #29 and that the AADL c_attr comment documents. The pull side (rs-matter DataModelHandler reading from this cache when a controller subscribes) is the next slice — same one that registers per-zone/contact/circuit endpoints with the right device-type descriptors.

What this PR ships

File Change
src/cache.rs (new) AttributeKey, AttributeValue { Bool / Int16 / Int64 / Float32 }, AttributeCache = Mutex<HashMap<...>> with set/get/len/is_empty. Shareable as Arc<AttributeCache>. 4 unit tests.
src/conversion.rs (new) convert_reading + convert_alert implementing the DESIGN.md §7.4 table: Temperature saturating cast, Power × 1000 (saturating), Concentration as f32, BooleanState bool. Alert-without-value semantics: Bool(true) on boolean clusters (the alert IS the trigger), None on numeric clusters (caller skips cache update). 10 unit tests.
src/rs_matter.rs RsMatterBridge gains Arc<AttributeCache> field + .cache() accessor. publish_* are now real: lookup mapping → convert → set cache. 6 new integration tests.
src/lib.rs Declares cache + conversion modules (not feature-gated; both work without rs-matter).

Test counts

cargo +1.87.0 test -p wohl-matter-bridge --lib                34/34  (was 19, +15)
cargo +1.87.0 test -p wohl-matter-bridge
  --features rs-matter-backend --lib                          43/43  (was 22, +21)

Oracles

cargo +1.87.0 fmt -p wohl-matter-bridge --check               OK
cargo +1.87.0 clippy -p wohl-matter-bridge -D warnings        OK (no features)
cargo +1.87.0 clippy ... --features rs-matter-backend         OK
cargo +1.87.0 test --workspace                                all binaries pass
cargo kani -p wohl-alert                                      4/4 harnesses
spar analyze (StarterHome.Deployed)                           0 errors
rivet validate                                                PASS

What's NOT in this PR (still)

  • The cache is written but not yet read by rs-matter. A commissioned controller subscribing to a bridged attribute today still sees only the root endpoint clusters (Basic Information, General Commissioning, Operational Credentials). The next slice wires:
    • Per-zone/contact/circuit Matter endpoint registration with the right device-type descriptor (ContactSensor 0x0015, WaterLeakDetector 0x0043, etc. per DESIGN.md §2),
    • A custom DataModelHandler that pulls from this cache when rs-matter asks for an attribute value,
    • The endpoint-id allocation policy decision (DESIGN.md §7.1 open question — u16 space, tagged-high-byte or register-on-demand).
  • The cache infrastructure stands on its own merits today: publish_* is non-panicking, the conversion contract is unit-tested, and the cache is observable from the bridge owner for test rigs or operator debug commands.

Verified line: still untouched

Zero edits to crates/wohl-{leak,temp,air,door,power,alert,ota,fw-door-bench}/, proofs/verus/, or fuzz/. Kani 4/4 on wohl-alert.

Test plan

  • CI green across all 14 jobs.
  • After merge: slice 6 — DataModelHandler + endpoint registration + cache-pull integration.

🤖 Generated with Claude Code

publish_reading and publish_alert no longer call unimplemented!().
The wohl bridge now keeps a thread-safe HashMap of the latest
Matter-encoded value per (endpoint, cluster, attribute) — mediating
the push/pull boundary between wohl's dispatcher (which produces
events) and rs-matter's DataModel (which polls for current values
when a controller subscribes).

What's in this slice

  src/cache.rs (new)
    - `AttributeKey { endpoint_id: u32, cluster_id: u32,
       attribute_id: u32 }` — wohl-side endpoint id; the Matter
       endpoint-id mapping (DESIGN.md §7.1) happens at the
       DataModel handler boundary in slice 6.
    - `AttributeValue` enum with variants matching Matter wire
       encodings: Bool (BooleanState), Int16 (Temperature centi-°C),
       Int64 (ActivePower milliwatts), Float32 (concentration
       clusters).
    - `AttributeCache` = `Mutex<HashMap<...>>`; thread-safe set/get;
       sharable as `Arc<AttributeCache>` so the future commissioning
       thread can hold its own handle.
    - 4 unit tests covering empty / set+get / overwrite / distinct
      keys.

  src/conversion.rs (new)
    - `convert_reading(kind, value: i64, mapping)` and
      `convert_alert(kind, Option<i64>, mapping)` apply the
      DESIGN.md §7.4 contract:
        Temperature: i64 centi-°C → Int16 (saturating cast)
        Power:       i64 watts    → Int64 mW (× 1000 saturating)
        CO2/PM2.5/VOC: i64 → Float32 (`as` cast)
        BooleanState (numeric reading): bool = (value != 0)
        BooleanState (alert, no value): always true (alert IS trigger)
    - 10 unit tests including saturation, mapping correctness,
      alert-without-value semantics, water-leak polarity.

  src/rs_matter.rs
    - `RsMatterBridge` gains an `Arc<AttributeCache>` field +
      `.cache()` accessor.
    - publish_reading: lookup mapping → convert → set cache.
    - publish_alert: lookup mapping → convert (None branch skips
      cache update for numeric clusters without a value) → derive
      endpoint_id from zone_id/contact_id/circuit_id → set cache.
    - 6 new integration tests verify publish path roundtrips
      (Temperature, WaterLeak, PowerSpike, HealthMiss drop,
      Freeze-without-value skip, Arc sharing).

  src/lib.rs
    - Declares cache + conversion modules (not feature-gated;
      both compile + test cleanly without rs-matter).

What's NOT in this slice (still)

  The cache is written but NOT YET READ by rs-matter's
  `DataModelHandler`. A commissioned controller subscribing to a
  bridged attribute today gets... nothing back from the bridge
  endpoints (the commissioning_only_handler in commissioning.rs
  has only the root endpoint). Slice 6 wires:
    - Per-zone/contact/circuit Matter endpoint registration with
      the right device-type descriptor (ContactSensor 0x0015,
      WaterLeakDetector 0x0043, etc. per DESIGN.md §2),
    - A custom DataModelHandler that pulls from this cache when
      rs-matter asks for an attribute value,
    - Endpoint-id allocation policy decision (DESIGN.md §7.1
      open question).

  Until then, the cache infrastructure stands on its own merits:
  publish_* is non-panicking, the conversion contract is unit-
  tested, and the cache is observable from the bridge owner for
  debugging or test-rig wiring.

Oracles green

  cargo +1.87.0 fmt -p wohl-matter-bridge --check               OK
  cargo +1.87.0 clippy -p wohl-matter-bridge -D warnings        OK (no features)
  cargo +1.87.0 clippy ... --features rs-matter-backend         OK
  cargo +1.87.0 test -p wohl-matter-bridge --lib                34/34 (feature off; was 19, +15 cache+conversion+wiring)
  cargo +1.87.0 test ... --features rs-matter-backend --lib     43/43 (feature on; was 22, +21)
  cargo +1.87.0 test --workspace                                21 test binaries pass
  cargo kani -p wohl-alert                                      4/4 harnesses
  spar analyze (StarterHome.Deployed)                           0 errors
  rivet validate                                                PASS (1 pre-existing warning)

Verified line: untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@avrabe avrabe merged commit ec08e70 into main May 28, 2026
14 checks passed
@avrabe avrabe deleted the 0.3.0/attribute-cache branch May 28, 2026 04:23
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