Require device id for Roku entities (#98734)

This commit is contained in:
Chris Talkington 2023-08-22 02:22:46 -05:00 committed by GitHub
parent 0f2b8570d2
commit 2e0038b981
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 58 additions and 43 deletions

View File

@ -22,7 +22,12 @@ PLATFORMS = [
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Roku from a config entry.""" """Set up Roku from a config entry."""
coordinator = RokuDataUpdateCoordinator(hass, host=entry.data[CONF_HOST]) if (device_id := entry.unique_id) is None:
device_id = entry.entry_id
coordinator = RokuDataUpdateCoordinator(
hass, host=entry.data[CONF_HOST], device_id=device_id
)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

View File

@ -71,10 +71,9 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up a Roku binary sensors based on a config entry.""" """Set up a Roku binary sensors based on a config entry."""
coordinator = hass.data[DOMAIN][entry.entry_id] coordinator = hass.data[DOMAIN][entry.entry_id]
unique_id = coordinator.data.info.serial_number
async_add_entities( async_add_entities(
RokuBinarySensorEntity( RokuBinarySensorEntity(
device_id=unique_id,
coordinator=coordinator, coordinator=coordinator,
description=description, description=description,
) )

View File

@ -32,8 +32,10 @@ class RokuDataUpdateCoordinator(DataUpdateCoordinator[Device]):
hass: HomeAssistant, hass: HomeAssistant,
*, *,
host: str, host: str,
device_id: str,
) -> None: ) -> None:
"""Initialize global Roku data updater.""" """Initialize global Roku data updater."""
self.device_id = device_id
self.roku = Roku(host=host, session=async_get_clientsession(hass)) self.roku = Roku(host=host, session=async_get_clientsession(hass))
self.full_update_interval = timedelta(minutes=15) self.full_update_interval = timedelta(minutes=15)

View File

@ -12,45 +12,37 @@ from .const import DOMAIN
class RokuEntity(CoordinatorEntity[RokuDataUpdateCoordinator]): class RokuEntity(CoordinatorEntity[RokuDataUpdateCoordinator]):
"""Defines a base Roku entity.""" """Defines a base Roku entity."""
_attr_has_entity_name = True
def __init__( def __init__(
self, self,
*, *,
device_id: str | None,
coordinator: RokuDataUpdateCoordinator, coordinator: RokuDataUpdateCoordinator,
description: EntityDescription | None = None, description: EntityDescription | None = None,
) -> None: ) -> None:
"""Initialize the Roku entity.""" """Initialize the Roku entity."""
super().__init__(coordinator) super().__init__(coordinator)
self._device_id = device_id
if description is not None: if description is not None:
self.entity_description = description self.entity_description = description
self._attr_unique_id = f"{coordinator.device_id}_{description.key}"
else:
self._attr_unique_id = coordinator.device_id
if device_id is None: self._attr_device_info = DeviceInfo(
self._attr_name = f"{coordinator.data.info.name} {description.name}" identifiers={(DOMAIN, coordinator.device_id)},
connections={
if device_id is not None: (CONNECTION_NETWORK_MAC, mac_address)
self._attr_has_entity_name = True for mac_address in (
self.coordinator.data.info.wifi_mac,
if description is not None: self.coordinator.data.info.ethernet_mac,
self._attr_unique_id = f"{device_id}_{description.key}" )
else: if mac_address is not None
self._attr_unique_id = device_id },
name=self.coordinator.data.info.name,
self._attr_device_info = DeviceInfo( manufacturer=self.coordinator.data.info.brand,
identifiers={(DOMAIN, device_id)}, model=self.coordinator.data.info.model_name,
connections={ hw_version=self.coordinator.data.info.model_number,
(CONNECTION_NETWORK_MAC, mac_address) sw_version=self.coordinator.data.info.version,
for mac_address in ( suggested_area=self.coordinator.data.info.device_location,
self.coordinator.data.info.wifi_mac, )
self.coordinator.data.info.ethernet_mac,
)
if mac_address is not None
},
name=self.coordinator.data.info.name,
manufacturer=self.coordinator.data.info.brand,
model=self.coordinator.data.info.model_name,
hw_version=self.coordinator.data.info.model_number,
sw_version=self.coordinator.data.info.version,
suggested_area=self.coordinator.data.info.device_location,
)

View File

@ -85,11 +85,10 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up the Roku config entry.""" """Set up the Roku config entry."""
coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
unique_id = coordinator.data.info.serial_number
async_add_entities( async_add_entities(
[ [
RokuMediaPlayer( RokuMediaPlayer(
device_id=unique_id,
coordinator=coordinator, coordinator=coordinator,
) )
], ],

View File

@ -22,11 +22,10 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Load Roku remote based on a config entry.""" """Load Roku remote based on a config entry."""
coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
unique_id = coordinator.data.info.serial_number
async_add_entities( async_add_entities(
[ [
RokuRemote( RokuRemote(
device_id=unique_id,
coordinator=coordinator, coordinator=coordinator,
) )
], ],

View File

@ -122,14 +122,12 @@ async def async_setup_entry(
"""Set up Roku select based on a config entry.""" """Set up Roku select based on a config entry."""
coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
device: RokuDevice = coordinator.data device: RokuDevice = coordinator.data
unique_id = device.info.serial_number
entities: list[RokuSelectEntity] = [] entities: list[RokuSelectEntity] = []
for description in ENTITIES: for description in ENTITIES:
entities.append( entities.append(
RokuSelectEntity( RokuSelectEntity(
device_id=unique_id,
coordinator=coordinator, coordinator=coordinator,
description=description, description=description,
) )
@ -138,7 +136,6 @@ async def async_setup_entry(
if len(device.channels) > 0: if len(device.channels) > 0:
entities.append( entities.append(
RokuSelectEntity( RokuSelectEntity(
device_id=unique_id,
coordinator=coordinator, coordinator=coordinator,
description=CHANNEL_ENTITY, description=CHANNEL_ENTITY,
) )

View File

@ -56,10 +56,9 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up Roku sensor based on a config entry.""" """Set up Roku sensor based on a config entry."""
coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: RokuDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
unique_id = coordinator.data.info.serial_number
async_add_entities( async_add_entities(
RokuSensorEntity( RokuSensorEntity(
device_id=unique_id,
coordinator=coordinator, coordinator=coordinator,
description=description, description=description,
) )

View File

@ -81,9 +81,13 @@ def mock_roku(
@pytest.fixture @pytest.fixture
async def init_integration( async def init_integration(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_roku: MagicMock hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_device: RokuDevice,
mock_roku: MagicMock,
) -> MockConfigEntry: ) -> MockConfigEntry:
"""Set up the Roku integration for testing.""" """Set up the Roku integration for testing."""
mock_config_entry.unique_id = mock_device.info.serial_number
mock_config_entry.add_to_hass(hass) mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.config_entries.async_setup(mock_config_entry.entry_id)

View File

@ -26,6 +26,25 @@ async def test_config_entry_not_ready(
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
async def test_config_entry_no_unique_id(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_roku: AsyncMock,
) -> None:
"""Test the Roku configuration entry with missing unique id."""
mock_config_entry.unique_id = None
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.entry_id in hass.data[DOMAIN]
assert mock_config_entry.state is ConfigEntryState.LOADED
assert (
hass.data[DOMAIN][mock_config_entry.entry_id].device_id
== mock_config_entry.entry_id
)
async def test_load_unload_config_entry( async def test_load_unload_config_entry(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,