Skip to content

core:SupportedAliases can list multiple entries with the same type (e.g. favorite1); add a helper to resolve the one the app would use #2224

Description

@iMicknl

Summary

core:SupportedAliases is not guaranteed to have one entry per type. Some devices report several alias ids sharing the same type (most commonly favorite1), each advertising a different features subset. Consumers that treat the array as "one alias per type" (e.g. creating one UI button per entry, or hardcoding an id) end up with duplicate or wrong behavior.

Real-world example

A Somfy io2way outdoor Venetian Blind (ogp:VenetianBlind) reports 6 separate favorite1 slots for what is functionally a single "My position" preset:

{
  "name": "core:SupportedAliases",
  "type": 10,
  "value": [
    { "id": "6", "type": "favorite1", "features": ["tiltPosition", "openClosePosition"] },
    { "id": "4", "type": "favorite1", "features": ["tiltPosition", "openClosePosition"] },
    { "id": "1", "type": "favorite1", "features": ["openClosePosition"] },
    { "id": "2", "type": "favorite1", "features": ["tiltPosition"] },
    { "id": "3", "type": "favorite1", "features": ["openClosePosition", "tiltPosition"] },
    { "id": "5", "type": "favorite1", "features": ["openClosePosition", "tiltPosition"] }
  ]
}

(full device dump: https://gist.github.com/iMicknl/d46cd2f2d690cfc2bfb7ee99e39e65bf#file-gistfile1-txt-L238)

This isn't hypothetical — it's already present in Home Assistant's Overkiz test fixtures for an unrelated device (bedroom_venetian_blind in cloud_somfy_tahoma_v2_europe.json), where a naive "one button per alias entry" implementation created 6 duplicate, identically-named "My position" buttons instead of 1 (home-assistant/core#175567).

How the official Somfy/TaHoma app handles this

The app never shows more than one button per alias type — it resolves to a single id at read time:

  1. Parse core:SupportedAliases into (id, type, features) entries. Entries whose type doesn't map to a known alias type (favorite1, favorite2, favorite3, pedestrian, partial, scene, security, ventilation) are dropped.
  2. Filter by requested type — e.g. favorite1 for the single "My position" button.
  3. Filter by a required capability set, tried in priority order from most to least capable (for a cover: openClosePosition+tiltPosition combo → openClose+tilt combo → tiltPosition alone → tilt alone → openClose alone). The first capability tier that has at least one matching alias wins; lower tiers are never consulted.
  4. Among the aliases that matched that tier, pick the one with the largest total features count ("most featured"). Ties are broken by first occurrence in the array — not by numeric id. (For the example above, ids 6, 4, 3, 5 all tie at 2 features; 6 wins because it appears first.)
  5. Use that single resolved id for both the goToAlias command (button press) and saveAlias command (long-press "set My position"). The other ids for that type are never surfaced in the UI.

Possible solution for python-overkiz-api

Right now core:SupportedAliases is exposed as a raw, untyped list (Attribute.value), leaving every consumer to reimplement this resolution — or, more likely, not implement it at all and hit the duplicate-button bug.

Proposal:

  1. Add a small typed model, e.g.:
    @dataclass
    class SupportedAlias:
        id: str
        type: str
        features: list[str]
  2. Add a helper (free function or Device method) that mirrors the app's resolution:
    def get_most_featured_alias(
        aliases: list[SupportedAlias],
        alias_type: str,
        required_features: list[str] | None = None,
    ) -> SupportedAlias | None:
        """Return the alias of alias_type with the most features.
    
        If required_features is given, only consider aliases whose features
        is a superset of it. Ties are broken by array order (first wins),
        matching the official app's resolution of its single favorite button.
        """
    Or, simpler and sufficient for most consumers (dedup only, no capability filtering): a helper that just groups by type and returns the most-featured alias per group, e.g. get_most_featured_alias_by_type(aliases) -> dict[str, SupportedAlias].
  3. Expose this on Device (e.g. device.get_supported_aliases() returning the typed list, plus the helper above), so integrations like Home Assistant can call one function instead of iterating the raw attribute and re-deriving this logic themselves.

Happy to open a PR for this if the approach sounds right — wanted to check the API shape first since it affects how downstream integrations would consume it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions