mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Fix via_device race in directv (#177712)
This commit is contained in:
@@ -8,8 +8,11 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE]
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
@@ -28,6 +31,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: DirecTVConfigEntry) -> b
|
||||
|
||||
entry.runtime_data = dtv
|
||||
|
||||
# Register the receiver device so client entities can link to it via_device_id.
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, dtv.device.info.receiver_id)},
|
||||
manufacturer=dtv.device.info.brand,
|
||||
name=next(
|
||||
(
|
||||
str.title(location.name)
|
||||
for location in dtv.device.locations
|
||||
if not location.client
|
||||
),
|
||||
None,
|
||||
),
|
||||
sw_version=dtv.device.info.version,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
from directv import DIRECTV
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import DirecTVConfigEntry
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
@@ -14,16 +17,32 @@ class DIRECTVEntity(Entity):
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
hass: HomeAssistant,
|
||||
dtv: DIRECTV,
|
||||
entry: DirecTVConfigEntry,
|
||||
name: str,
|
||||
address: str = "0",
|
||||
) -> None:
|
||||
"""Initialize the DirecTV entity."""
|
||||
self._address = address
|
||||
self._device_id = address if address != "0" else dtv.device.info.receiver_id
|
||||
self._is_client = address != "0"
|
||||
self.dtv = dtv
|
||||
via_device_id: str | None = None
|
||||
if self._is_client:
|
||||
via_device_id = dr.async_get_device_id_by_identifier(
|
||||
hass,
|
||||
(DOMAIN, dtv.device.info.receiver_id),
|
||||
config_entry_id=entry.entry_id,
|
||||
)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device_id)},
|
||||
manufacturer=self.dtv.device.info.brand,
|
||||
manufacturer=dtv.device.info.brand,
|
||||
name=name,
|
||||
sw_version=self.dtv.device.info.version,
|
||||
via_device=(DOMAIN, self.dtv.device.info.receiver_id),
|
||||
sw_version=dtv.device.info.version,
|
||||
)
|
||||
if via_device_id is not None:
|
||||
self._attr_device_info["via_device_id"] = via_device_id
|
||||
|
||||
@@ -61,7 +61,9 @@ async def async_setup_entry(
|
||||
async_add_entities(
|
||||
(
|
||||
DIRECTVMediaPlayer(
|
||||
hass=hass,
|
||||
dtv=dtv,
|
||||
entry=entry,
|
||||
name=str.title(location.name),
|
||||
address=location.address,
|
||||
)
|
||||
@@ -74,10 +76,20 @@ async def async_setup_entry(
|
||||
class DIRECTVMediaPlayer(DIRECTVEntity, MediaPlayerEntity):
|
||||
"""Representation of a DirecTV receiver on the network."""
|
||||
|
||||
def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
hass: HomeAssistant,
|
||||
dtv: DIRECTV,
|
||||
entry: DirecTVConfigEntry,
|
||||
name: str,
|
||||
address: str = "0",
|
||||
) -> None:
|
||||
"""Initialize DirecTV media player."""
|
||||
super().__init__(
|
||||
hass=hass,
|
||||
dtv=dtv,
|
||||
entry=entry,
|
||||
name=name,
|
||||
address=address,
|
||||
)
|
||||
|
||||
@@ -29,7 +29,9 @@ async def async_setup_entry(
|
||||
async_add_entities(
|
||||
(
|
||||
DIRECTVRemote(
|
||||
hass=hass,
|
||||
dtv=dtv,
|
||||
entry=entry,
|
||||
name=str.title(location.name),
|
||||
address=location.address,
|
||||
)
|
||||
@@ -42,10 +44,20 @@ async def async_setup_entry(
|
||||
class DIRECTVRemote(DIRECTVEntity, RemoteEntity):
|
||||
"""Device that sends commands to a DirecTV receiver."""
|
||||
|
||||
def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
hass: HomeAssistant,
|
||||
dtv: DIRECTV,
|
||||
entry: DirecTVConfigEntry,
|
||||
name: str,
|
||||
address: str = "0",
|
||||
) -> None:
|
||||
"""Initialize DirecTV remote."""
|
||||
super().__init__(
|
||||
hass=hass,
|
||||
dtv=dtv,
|
||||
entry=entry,
|
||||
name=name,
|
||||
address=address,
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ from unittest.mock import patch
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.directv.const import DOMAIN
|
||||
from homeassistant.components.directv.media_player import (
|
||||
ATTR_MEDIA_CURRENTLY_RECORDING,
|
||||
ATTR_MEDIA_RATING,
|
||||
@@ -46,10 +47,10 @@ from homeassistant.const import (
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import setup_integration
|
||||
from . import RECEIVER_ID, setup_integration
|
||||
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
@@ -162,6 +163,29 @@ async def test_unique_id(
|
||||
assert unavailable_client.unique_id == "9XXXXXXXXXX9"
|
||||
|
||||
|
||||
async def test_client_device_via_device_id(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
) -> None:
|
||||
"""Test a client's device links to the receiver device via via_device_id."""
|
||||
entry = await setup_integration(hass, aioclient_mock)
|
||||
|
||||
receiver_device = device_registry.async_get_device_by_identifier(
|
||||
(DOMAIN, RECEIVER_ID), entry.entry_id
|
||||
)
|
||||
assert receiver_device is not None
|
||||
|
||||
client_entity = entity_registry.async_get(CLIENT_ENTITY_ID)
|
||||
assert client_entity is not None
|
||||
assert client_entity.device_id is not None
|
||||
|
||||
client_device = device_registry.async_get(client_entity.device_id)
|
||||
assert client_device is not None
|
||||
assert client_device.via_device_id == receiver_device.id
|
||||
|
||||
|
||||
async def test_supported_features(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user