Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or capability

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions