Skip to content

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

@iMicknl

Description

@iMicknl

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:

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:
    cast(float, device.states.get_value(OverkizState.CORE_TARGET_DWH_TEMPERATURE))
  2. get() + None-guard + value_as_*:
    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:

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

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions