feat(displays): per-display devices + DDC/CI capability entities
Restructure how displays are exposed in Home Assistant:
Each physical monitor is now its own HA device linked to the media-server
hub via `via_device`. The hub keeps the media_player + script buttons; per-
display devices hold the power switch, brightness slider, and the new
capability entities. This lets users place displays in their own area/room
and keeps related entities grouped together in the UI.
New platforms:
- sensor: DisplayResolutionSensor (diagnostic, from EDID)
- binary_sensor: DisplayPrimaryBinarySensor + DisplayPowerControlBinarySensor
(both diagnostic; help users see why a power switch is or isn't created)
- select: DisplayInputSourceSelect (HDMI1/DP1/...), DisplayColorPresetSelect
(color temperature), DisplayPictureModeSelect (VCP 0xDC scene modes)
- number: added DisplayContrastNumber alongside brightness
Other changes:
- display_device helper centralises the per-display DeviceInfo; pulls real
manufacturer/model from EDID; device name no longer prepends the hub
title since via_device already shows the hierarchy.
- api_client gains set_display_{contrast,input_source,color_preset,picture_mode}
and stops forcing `?refresh=true` on every poll so HA can ride the
server's TTL cache instead of triggering full DDC/CI probes per entity.
- select / number entities now check the server's `success` flag and re-
sync from the actual monitor state when a write was silently rejected
(some monitors honor reads but ignore writes for certain DDC/CI codes).
Bumps manifest.json to 0.3.0 - the device topology change is user-visible
and existing brightness/power entities migrate to per-display devices on
first reload (unique_ids are preserved).
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
"""Helpers for building per-display DeviceInfo.
|
||||
|
||||
Each physical monitor is exposed as its own HA device (linked back to the
|
||||
media-server hub via `via_device`) so that per-display entities (power
|
||||
switch, brightness, future per-display sensors) cluster together, can be
|
||||
placed in their own area/room, and participate in device-based automations.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
def display_label(monitor: dict[str, Any]) -> str:
|
||||
"""Return a user-friendly label for a display monitor.
|
||||
|
||||
Resolution is appended when available so that two monitors sharing a
|
||||
name (e.g. two "Generic PnP Monitor" entries) remain distinguishable.
|
||||
"""
|
||||
name = monitor.get("name") or f"Monitor {monitor['id']}"
|
||||
resolution = monitor.get("resolution")
|
||||
if resolution:
|
||||
return f"{name} ({resolution})"
|
||||
return name
|
||||
|
||||
|
||||
def display_device_identifier(entry: ConfigEntry, monitor_id: int) -> tuple[str, str]:
|
||||
"""Return the stable identifier tuple for a per-display device."""
|
||||
return (DOMAIN, f"{entry.entry_id}_display_{monitor_id}")
|
||||
|
||||
|
||||
def display_device_info(entry: ConfigEntry, monitor: dict[str, Any]) -> DeviceInfo:
|
||||
"""Build DeviceInfo for a per-display device linked to the hub.
|
||||
|
||||
Prefers the manufacturer/model reported by the monitor's EDID; falls back
|
||||
to integration-level defaults so devices still appear sensibly even when
|
||||
EDID parsing returns blanks.
|
||||
"""
|
||||
manufacturer = (monitor.get("manufacturer") or "").strip() or "Remote Media Player"
|
||||
model = (monitor.get("model") or "").strip() or "Display"
|
||||
|
||||
return DeviceInfo(
|
||||
identifiers={display_device_identifier(entry, monitor["id"])},
|
||||
via_device=(DOMAIN, entry.entry_id),
|
||||
# HA's device tree already shows the parent hub above its children
|
||||
# via `via_device`, so re-stating the entry title here would just
|
||||
# duplicate the hub name on every child row.
|
||||
name=display_label(monitor),
|
||||
manufacturer=manufacturer,
|
||||
model=model,
|
||||
)
|
||||
Reference in New Issue
Block a user