Fan out targeted services to multiple RelayTV entries
Resolve all targeted RelayTV config entries for service calls so overlay, play, snapshot, resume, and other targeted actions execute across multiple selected Home Assistant entities or devices instead of only the first target.
This commit is contained in:
@@ -125,17 +125,15 @@ def _fallback_entry_id(hass: HomeAssistant) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_entry_id_for_call(hass: HomeAssistant, call: ServiceCall) -> str | None:
|
||||
def _target_entity_ids_for_call(hass: HomeAssistant, call: ServiceCall) -> list[str]:
|
||||
entity_ids: list[str] = []
|
||||
raw_entity = call.data.get(CONF_ENTITY_ID)
|
||||
if isinstance(raw_entity, str):
|
||||
entity_ids = [raw_entity]
|
||||
entity_ids.append(raw_entity)
|
||||
elif isinstance(raw_entity, list):
|
||||
entity_ids = [item for item in raw_entity if isinstance(item, str)]
|
||||
entity_ids.extend(item for item in raw_entity if isinstance(item, str))
|
||||
|
||||
registry = er.async_get(hass)
|
||||
|
||||
if not entity_ids:
|
||||
device_id = call.data.get("device_id")
|
||||
device_ids = [device_id] if isinstance(device_id, str) else device_id
|
||||
if isinstance(device_ids, list):
|
||||
@@ -146,15 +144,31 @@ def _resolve_entry_id_for_call(hass: HomeAssistant, call: ServiceCall) -> str |
|
||||
if reg_entry.entity_id.startswith("media_player."):
|
||||
entity_ids.append(reg_entry.entity_id)
|
||||
|
||||
for entity_id in entity_ids:
|
||||
return list(dict.fromkeys(entity_ids))
|
||||
|
||||
|
||||
def _resolve_entry_ids_for_call(hass: HomeAssistant, call: ServiceCall) -> list[str]:
|
||||
registry = er.async_get(hass)
|
||||
entry_ids: list[str] = []
|
||||
for entity_id in _target_entity_ids_for_call(hass, call):
|
||||
reg_entry = registry.async_get(entity_id)
|
||||
if reg_entry and _get_entry_data(hass, reg_entry.config_entry_id):
|
||||
return reg_entry.config_entry_id
|
||||
entry_ids.append(reg_entry.config_entry_id)
|
||||
|
||||
if entry_ids:
|
||||
return list(dict.fromkeys(entry_ids))
|
||||
|
||||
panel_target = hass.data.get(DOMAIN, {}).get(DATA_PANEL_SETTINGS, {}).get(CONF_PANEL_TARGET_ENTRY_ID)
|
||||
if panel_target and _get_entry_data(hass, panel_target):
|
||||
return panel_target
|
||||
return _fallback_entry_id(hass)
|
||||
return [panel_target]
|
||||
|
||||
fallback = _fallback_entry_id(hass)
|
||||
return [fallback] if fallback else []
|
||||
|
||||
|
||||
def _resolve_entry_id_for_call(hass: HomeAssistant, call: ServiceCall) -> str | None:
|
||||
entry_ids = _resolve_entry_ids_for_call(hass, call)
|
||||
return entry_ids[0] if entry_ids else None
|
||||
|
||||
|
||||
def _resolve_entries_for_entities(hass: HomeAssistant, entity_ids: list[str]) -> list[str]:
|
||||
@@ -318,10 +332,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
url = (call.data.get("url") or "").strip()
|
||||
if not url:
|
||||
return
|
||||
entry_id = _resolve_entry_id_for_call(hass, call)
|
||||
store = _get_entry_data(hass, entry_id) if entry_id else None
|
||||
for entry_id in _resolve_entry_ids_for_call(hass, call):
|
||||
store = _get_entry_data(hass, entry_id)
|
||||
if not store:
|
||||
return
|
||||
continue
|
||||
await store[DATA_API].smart_url(url)
|
||||
await store[DATA_COORDINATOR].async_request_refresh()
|
||||
|
||||
@@ -329,10 +343,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
url = (call.data.get("url") or "").strip()
|
||||
if not url:
|
||||
return
|
||||
entry_id = _resolve_entry_id_for_call(hass, call)
|
||||
store = _get_entry_data(hass, entry_id) if entry_id else None
|
||||
for entry_id in _resolve_entry_ids_for_call(hass, call):
|
||||
store = _get_entry_data(hass, entry_id)
|
||||
if not store:
|
||||
return
|
||||
continue
|
||||
await store[DATA_API].play(url=url, use_ytdlp=call.data.get("use_ytdlp"), cec=call.data.get("cec"))
|
||||
await store[DATA_COORDINATOR].async_request_refresh()
|
||||
|
||||
@@ -343,10 +357,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
url = (call.data.get("url") or "").strip()
|
||||
if not url:
|
||||
return
|
||||
entry_id = _resolve_entry_id_for_call(hass, call)
|
||||
store = _get_entry_data(hass, entry_id) if entry_id else None
|
||||
for entry_id in _resolve_entry_ids_for_call(hass, call):
|
||||
store = _get_entry_data(hass, entry_id)
|
||||
if not store:
|
||||
return
|
||||
continue
|
||||
await store[DATA_API].play_temporary(
|
||||
url=url,
|
||||
timeout_sec=call.data.get("timeout"),
|
||||
@@ -355,10 +369,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
await store[DATA_COORDINATOR].async_request_refresh()
|
||||
|
||||
async def _handle_overlay(call: ServiceCall):
|
||||
entry_id = _resolve_entry_id_for_call(hass, call)
|
||||
store = _get_entry_data(hass, entry_id) if entry_id else None
|
||||
for entry_id in _resolve_entry_ids_for_call(hass, call):
|
||||
store = _get_entry_data(hass, entry_id)
|
||||
if not store:
|
||||
return
|
||||
continue
|
||||
await store[DATA_API].overlay(
|
||||
text=call.data.get("text"),
|
||||
duration=call.data.get("duration"),
|
||||
@@ -372,13 +386,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return
|
||||
delay = float(call.data.get("delay_buffer_sec") or 2)
|
||||
start_at = time.time() + delay
|
||||
targets = call.data.get("target_entities") or call.data.get(CONF_ENTITY_ID) or []
|
||||
targets = call.data.get("target_entities")
|
||||
entry_ids: list[str] = []
|
||||
if isinstance(targets, str):
|
||||
targets = [targets]
|
||||
if isinstance(targets, list):
|
||||
entry_ids = _resolve_entries_for_entities(hass, [e for e in targets if isinstance(e, str)])
|
||||
if not entry_ids:
|
||||
fallback = _resolve_entry_id_for_call(hass, call)
|
||||
entry_ids = [fallback] if fallback else []
|
||||
entry_ids = _resolve_entry_ids_for_call(hass, call)
|
||||
for entry_id in entry_ids:
|
||||
store = _get_entry_data(hass, entry_id)
|
||||
if not store:
|
||||
@@ -387,10 +402,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
await store[DATA_COORDINATOR].async_request_refresh()
|
||||
|
||||
async def _handle_snapshot(call: ServiceCall):
|
||||
entry_id = _resolve_entry_id_for_call(hass, call)
|
||||
store = _get_entry_data(hass, entry_id) if entry_id else None
|
||||
for entry_id in _resolve_entry_ids_for_call(hass, call):
|
||||
store = _get_entry_data(hass, entry_id)
|
||||
if not store:
|
||||
return
|
||||
continue
|
||||
data = await store[DATA_API].snapshot() or {}
|
||||
snapshot_url = data.get("image_url") if isinstance(data, dict) else None
|
||||
snapshot_url = _absolute_url(store[DATA_API].base_url, snapshot_url)
|
||||
@@ -402,12 +417,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
url = (call.data.get("url") or "").strip()
|
||||
if not url:
|
||||
return
|
||||
entry_id = _resolve_entry_id_for_call(hass, call)
|
||||
store = _get_entry_data(hass, entry_id) if entry_id else None
|
||||
if not store:
|
||||
return
|
||||
runtime = await _async_load_runtime_data(hass)
|
||||
resume_position = runtime.get(CONF_RESUME_POSITIONS, {}).get(url)
|
||||
for entry_id in _resolve_entry_ids_for_call(hass, call):
|
||||
store = _get_entry_data(hass, entry_id)
|
||||
if not store:
|
||||
continue
|
||||
await store[DATA_API].play(url=url, use_ytdlp=call.data.get("use_ytdlp"), cec=call.data.get("cec"))
|
||||
if resume_position is not None:
|
||||
await store[DATA_API].seek_abs(float(resume_position))
|
||||
|
||||
Reference in New Issue
Block a user