iMicknl / iMicknl/python-overkiz-api

Add typed value getters to States container (get_value_as_*/first_value_as_*)

Open
#2,113 0 comments 0 reactions 0 assignees View on GitHub
feature
Dominant language
Python
Stars
58
Forks
35
Avg merge
2d 14h
Merged PRs (30d)
15

Description

## 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.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in pyoverkiz/models.py by reading the existing States get_value/first_value methods and State.value_as_* properties. Add the typed getter and fallback variants described in the issue, then extend tests/test_models.py for hits, misses, type mismatches, and enum keys. Update docs/device-control.md alongside get_value; done means the tests pass and the new accessors are documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, documentation, testing
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.