## Summary Complete the v2 `States` accessor ergonomics pass (started in #2108) by adding **typed value getters** to the `States` container, before the 2.0 freeze. `States.get_value()` / `first_value()` return the untyped union: ```python StateType = str | int | float | bool | dict[str, Any] | list[Any] | None ``` The typed accessors (`value_as_float`, `value_as_int`, …) exist only on the `State` object. So every consumer that needs a concrete type is pushed into one of two verbose shapes: 1. **Cast the union:** ```python cast(float, device.states.get_value(OverkizState.CORE_TARGET_DWH_TEMPERATURE)) ``` 2. **`get()` + None-guard + `value_as_*`:** ```python state = device.states.get(name) if state is None: return None position = state.value_as_int ``` In the Home Assistant `overkiz` integration's pyOverkiz 2.0 migration this accounts for **52 `cast(...)` call sites across 23 files** (~26 `float`, ~22 `str`, ~3 `dict`, ~1 `int`), plus the two-step guard pattern in cover position fallbacks and every water-heater / Hitachi temperature property. ## Proposed addition Thin pass-throughs delegating to the **already-existing, already-tested** `State.value_as_*` properties: ```python # on States (backs both device.states and device.attributes) def get_value_as_float(self, name: StateName) -> float | None: state = self._index.get(name) return state.value_as_float if state is not None else None # ... get_value_as_int / _str / _bool / _dict / _list # ... first_value_as_float(names) / ... for fallback chains ``` Usage collapses to: ```python temp = device.states.get_value_as_float(OverkizState.CORE_TARGET_DWH_TEMPERATURE) position = device.states.get_value_as_int(state_name) # None if missing ``` ## Why before the 2.0 freeze The addition is **non-breaking**, but it is freeze-sensitive: - The new public **method names are locked at freeze** — renaming them post-2.0 would itself be a breaking change. - Consumers that pin an exact version (e.g. Home Assistant) need the methods in the `2.0.0` final to adopt them in a **single migration pass** rather than re-touching every file again in 2.1. ## Design decisions to lock - **Naming:** `get_value_as_float` (mirrors `State.value_as_float`) vs a shorter `get_float`. Whatever is chosen is frozen. - **Fallback variant:** include `first_value_as_*` — serves fallback chains (e.g. cover "My position" / "Unknown position", towel-dryer setpoint selection). - **Mismatch semantics:** `State.value_as_*` already raises `TypeError` on a genuine type mismatch (fail-fast). Keep that propagating; only a *missing* state returns `None`. ## Scope - `pyoverkiz/models.py`: add the getters to `States` + the `first_value_as_*` variants. - `tests/test_models.py`: cover hit / miss / type-mismatch / enum-key cases. - `docs/device-control.md`: document the new getters alongside `get_value`. Follow-up (separate, in home-assistant-core): adopt the getters and drop the 52 casts. Relates to #2108.