Skip to content

Cover

The cover accessor returns Cover entities — garage doors, blinds, shades, awnings. The HA cover domain conflates "open / close" with "set position to N%"; HaClient exposes both clearly.

Reading state

cover = ha.cover("garage")

print(cover.is_open)            # state == "open"
print(cover.is_closed)          # state == "closed"
print(cover.current_position)   # 0-100 (closed-open), or None

state is a string and may also be "opening", "closing", or "unknown". is_open / is_closed only check the steady-state strings — neither is True mid-motion.

Opening, closing, stopping

await cover.open()
await cover.close()
await cover.stop()       # stop a motion in progress
await cover.toggle()     # open if closed, close otherwise

stop() is a no-op for covers that do not support stopping.

Setting a specific position

await cover.set_position(50)     # half-open
await cover.set_position(100)    # fully open (equivalent to open())
await cover.set_position(0)      # fully closed (equivalent to close())

position is 0..100. Covers that do not support intermediate positions will round to the nearest supported value (typically 0 or 100).

Reacting to changes

@cover.on_open
def opened(old: str | None, new: str | None) -> None:
    print("garage is now open")

@cover.on_close
def closed(old, new): ...

@cover.on_position_change
def position(old: int | None, new: int | None) -> None:
    print(f"garage position {old} -> {new}")

on_open / on_close fire on the transition into the open or closed state — not on each tick of motion. Use on_position_change if you need every percentage update.

Common patterns

Open only if currently closed

if cover.is_closed:
    await cover.open()

Vent — open partially, then close again

await cover.set_position(20)
await asyncio.sleep(300)
await cover.close()

Sync two covers

target = ha.cover("blind_left").current_position or 100
await ha.cover("blind_right").set_position(target)