From e485e0c5dbc1f5b1c35d4efef321ceefaee8cc50 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Thu, 2 Apr 2020 23:20:17 -0500 Subject: [PATCH 1/5] Debounce calls to Plex server --- homeassistant/components/plex/const.py | 1 + homeassistant/components/plex/server.py | 36 ++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/plex/const.py b/homeassistant/components/plex/const.py index d5cb3db3aba745..6c2a7fd7debfcc 100644 --- a/homeassistant/components/plex/const.py +++ b/homeassistant/components/plex/const.py @@ -9,6 +9,7 @@ DEFAULT_SSL = False DEFAULT_VERIFY_SSL = True +DEBOUNCE_TIMEOUT = 0.5 DISPATCHERS = "dispatchers" PLATFORMS = frozenset(["media_player", "sensor"]) PLATFORMS_COMPLETED = "platforms_completed" diff --git a/homeassistant/components/plex/server.py b/homeassistant/components/plex/server.py index 196968cc097424..df8a5949a22cf4 100644 --- a/homeassistant/components/plex/server.py +++ b/homeassistant/components/plex/server.py @@ -1,4 +1,5 @@ """Shared class to maintain Plex server instances.""" +from functools import partial, wraps import logging import ssl from urllib.parse import urlparse @@ -11,7 +12,9 @@ from homeassistant.components.media_player import DOMAIN as MP_DOMAIN from homeassistant.const import CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL +from homeassistant.core import callback from homeassistant.helpers.dispatcher import dispatcher_send +from homeassistant.helpers.event import async_call_later from .const import ( CONF_CLIENT_IDENTIFIER, @@ -19,6 +22,7 @@ CONF_MONITORED_USERS, CONF_SERVER, CONF_USE_EPISODE_ART, + DEBOUNCE_TIMEOUT, DEFAULT_VERIFY_SSL, PLEX_NEW_MP_SIGNAL, PLEX_UPDATE_MEDIA_PLAYER_SIGNAL, @@ -39,12 +43,34 @@ plexapi.X_PLEX_VERSION = X_PLEX_VERSION +def debounce(func): + """Decorate function to debounce callbacks from Plex websocket.""" + + @callback + def call_later_listener(self, _): + """Handle call_later callback.""" + self.debounce = None + self.hass.async_add_executor_job(func, self) + + @wraps(func) + def wrapper(self): + """Schedule async callback.""" + if self.debounce: + self.debounce() + remove_listener = async_call_later( + self.hass, DEBOUNCE_TIMEOUT, partial(call_later_listener, self), + ) + self.debounce = remove_listener + + return wrapper + + class PlexServer: """Manages a single Plex server connection.""" def __init__(self, hass, server_config, known_server_id=None, options=None): """Initialize a Plex server instance.""" - self._hass = hass + self.hass = hass self._plex_server = None self._known_clients = set() self._known_idle = set() @@ -58,6 +84,7 @@ def __init__(self, hass, server_config, known_server_id=None, options=None): self._accounts = [] self._owner_username = None self._version = None + self.debounce = None # Header conditionally added as it is not available in config entry v1 if CONF_CLIENT_IDENTIFIER in server_config: @@ -150,12 +177,13 @@ def refresh_entity(self, machine_identifier, device, session): unique_id = f"{self.machine_identifier}:{machine_identifier}" _LOGGER.debug("Refreshing %s", unique_id) dispatcher_send( - self._hass, + self.hass, PLEX_UPDATE_MEDIA_PLAYER_SIGNAL.format(unique_id), device, session, ) + @debounce def update_platforms(self): """Update the platform entities.""" _LOGGER.debug("Updating devices") @@ -239,13 +267,13 @@ def update_platforms(self): if new_entity_configs: dispatcher_send( - self._hass, + self.hass, PLEX_NEW_MP_SIGNAL.format(self.machine_identifier), new_entity_configs, ) dispatcher_send( - self._hass, + self.hass, PLEX_UPDATE_SENSOR_SIGNAL.format(self.machine_identifier), sessions, ) From 2cd621398e3ccb6c33e2c92df7cf2237b78ebfa3 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Fri, 3 Apr 2020 15:09:34 -0500 Subject: [PATCH 2/5] Simplify debounce by recommendation --- homeassistant/components/plex/const.py | 2 +- homeassistant/components/plex/server.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/plex/const.py b/homeassistant/components/plex/const.py index 6c2a7fd7debfcc..555454e22050f0 100644 --- a/homeassistant/components/plex/const.py +++ b/homeassistant/components/plex/const.py @@ -9,7 +9,7 @@ DEFAULT_SSL = False DEFAULT_VERIFY_SSL = True -DEBOUNCE_TIMEOUT = 0.5 +DEBOUNCE_TIMEOUT = 1 DISPATCHERS = "dispatchers" PLATFORMS = frozenset(["media_player", "sensor"]) PLATFORMS_COMPLETED = "platforms_completed" diff --git a/homeassistant/components/plex/server.py b/homeassistant/components/plex/server.py index df8a5949a22cf4..e8e9a1c8539b68 100644 --- a/homeassistant/components/plex/server.py +++ b/homeassistant/components/plex/server.py @@ -12,7 +12,6 @@ from homeassistant.components.media_player import DOMAIN as MP_DOMAIN from homeassistant.const import CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL -from homeassistant.core import callback from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_call_later @@ -46,21 +45,23 @@ def debounce(func): """Decorate function to debounce callbacks from Plex websocket.""" - @callback - def call_later_listener(self, _): + unsub = None + + async def call_later_listener(self, _): """Handle call_later callback.""" - self.debounce = None - self.hass.async_add_executor_job(func, self) + nonlocal unsub + unsub = None + await self.hass.async_add_executor_job(func, self) @wraps(func) def wrapper(self): """Schedule async callback.""" - if self.debounce: - self.debounce() - remove_listener = async_call_later( + nonlocal unsub + if unsub: + unsub() # pylint: disable=not-callable + unsub = async_call_later( self.hass, DEBOUNCE_TIMEOUT, partial(call_later_listener, self), ) - self.debounce = remove_listener return wrapper @@ -84,7 +85,6 @@ def __init__(self, hass, server_config, known_server_id=None, options=None): self._accounts = [] self._owner_username = None self._version = None - self.debounce = None # Header conditionally added as it is not available in config entry v1 if CONF_CLIENT_IDENTIFIER in server_config: From d6a58a8e0cb9e9c8f233c42f0f015b97976b9c83 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Fri, 3 Apr 2020 15:30:03 -0500 Subject: [PATCH 3/5] Update tests to handle debounce --- tests/components/plex/common.py | 13 +++++++++++++ tests/components/plex/test_config_flow.py | 2 ++ tests/components/plex/test_init.py | 6 ++++++ tests/components/plex/test_server.py | 5 +++++ 4 files changed, 26 insertions(+) create mode 100644 tests/components/plex/common.py diff --git a/tests/components/plex/common.py b/tests/components/plex/common.py new file mode 100644 index 00000000000000..7879567c7ef66e --- /dev/null +++ b/tests/components/plex/common.py @@ -0,0 +1,13 @@ +"""Common fixtures and functions for Plex tests.""" +from datetime import timedelta + +from homeassistant.components.plex.const import DEBOUNCE_TIMEOUT +import homeassistant.util.dt as dt_util + +from tests.common import async_fire_time_changed + + +def tick_debounce_timeout(hass): + """Advance test clock by debounce timeout.""" + next_update = dt_util.utcnow() + timedelta(seconds=DEBOUNCE_TIMEOUT) + async_fire_time_changed(hass, next_update) diff --git a/tests/components/plex/test_config_flow.py b/tests/components/plex/test_config_flow.py index d839ccc674b839..2365b86e73ed06 100644 --- a/tests/components/plex/test_config_flow.py +++ b/tests/components/plex/test_config_flow.py @@ -23,6 +23,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.setup import async_setup_component +from .common import tick_debounce_timeout from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN from .mock_classes import MockPlexAccount, MockPlexServer @@ -417,6 +418,7 @@ async def test_option_flow_new_users_available(hass, caplog): server_id = mock_plex_server.machineIdentifier async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users diff --git a/tests/components/plex/test_init.py b/tests/components/plex/test_init.py index 387ce6cac03e0e..8d6eed9e3b8a09 100644 --- a/tests/components/plex/test_init.py +++ b/tests/components/plex/test_init.py @@ -27,6 +27,7 @@ from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util +from .common import tick_debounce_timeout from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN from .mock_classes import MockPlexAccount, MockPlexServer @@ -110,12 +111,14 @@ async def test_setup_with_config_entry(hass): ) async_dispatcher_send(hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() sensor = hass.states.get("sensor.plex_plex_server_1") assert sensor.state == str(len(mock_plex_server.accounts)) async_dispatcher_send(hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() with patch.object( @@ -124,6 +127,7 @@ async def test_setup_with_config_entry(hass): async_dispatcher_send( hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id) ) + tick_debounce_timeout(hass) await hass.async_block_till_done() with patch.object( @@ -132,6 +136,7 @@ async def test_setup_with_config_entry(hass): async_dispatcher_send( hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id) ) + tick_debounce_timeout(hass) await hass.async_block_till_done() @@ -295,6 +300,7 @@ async def test_setup_with_photo_session(hass): server_id = mock_plex_server.machineIdentifier async_dispatcher_send(hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() media_player = hass.states.get("media_player.plex_product_title") diff --git a/tests/components/plex/test_server.py b/tests/components/plex/test_server.py index 646a6ded32e64b..2f18bb1cf72f3a 100644 --- a/tests/components/plex/test_server.py +++ b/tests/components/plex/test_server.py @@ -13,6 +13,7 @@ ) from homeassistant.helpers.dispatcher import async_dispatcher_send +from .common import tick_debounce_timeout from .const import DEFAULT_DATA, DEFAULT_OPTIONS from .mock_classes import MockPlexServer @@ -45,6 +46,7 @@ async def test_new_users_available(hass): server_id = mock_plex_server.machineIdentifier async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users @@ -84,6 +86,7 @@ async def test_new_ignored_users_available(hass, caplog): server_id = mock_plex_server.machineIdentifier async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users @@ -119,6 +122,7 @@ async def test_mark_sessions_idle(hass): server_id = mock_plex_server.machineIdentifier async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() sensor = hass.states.get("sensor.plex_plex_server_1") @@ -128,6 +132,7 @@ async def test_mark_sessions_idle(hass): mock_plex_server.clear_sessions() async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + tick_debounce_timeout(hass) await hass.async_block_till_done() sensor = hass.states.get("sensor.plex_plex_server_1") From 19b776e5c0d5e6b6ff2261934684df96c53f3313 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Fri, 3 Apr 2020 21:25:12 -0500 Subject: [PATCH 4/5] Test debouncer, fix & optimize tests --- homeassistant/components/plex/server.py | 1 + tests/components/plex/common.py | 13 +++-- tests/components/plex/test_config_flow.py | 8 +-- tests/components/plex/test_init.py | 42 +++++++--------- tests/components/plex/test_server.py | 60 +++++++++++++++++------ 5 files changed, 77 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/plex/server.py b/homeassistant/components/plex/server.py index e8e9a1c8539b68..a0b8b03670a9a8 100644 --- a/homeassistant/components/plex/server.py +++ b/homeassistant/components/plex/server.py @@ -58,6 +58,7 @@ def wrapper(self): """Schedule async callback.""" nonlocal unsub if unsub: + _LOGGER.debug("Throttling update of %s", self._plex_server.friendlyName) unsub() # pylint: disable=not-callable unsub = async_call_later( self.hass, DEBOUNCE_TIMEOUT, partial(call_later_listener, self), diff --git a/tests/components/plex/common.py b/tests/components/plex/common.py index 7879567c7ef66e..adc6f4e0299d49 100644 --- a/tests/components/plex/common.py +++ b/tests/components/plex/common.py @@ -1,13 +1,20 @@ """Common fixtures and functions for Plex tests.""" from datetime import timedelta -from homeassistant.components.plex.const import DEBOUNCE_TIMEOUT +from homeassistant.components.plex.const import ( + DEBOUNCE_TIMEOUT, + PLEX_UPDATE_PLATFORMS_SIGNAL, +) +from homeassistant.helpers.dispatcher import async_dispatcher_send import homeassistant.util.dt as dt_util from tests.common import async_fire_time_changed -def tick_debounce_timeout(hass): - """Advance test clock by debounce timeout.""" +async def trigger_plex_update(hass, server_id): + """Update Plex by sending signal and jumping ahead by debounce timeout.""" + async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + await hass.async_block_till_done() next_update = dt_util.utcnow() + timedelta(seconds=DEBOUNCE_TIMEOUT) async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() diff --git a/tests/components/plex/test_config_flow.py b/tests/components/plex/test_config_flow.py index 2365b86e73ed06..bd5d45c0246b71 100644 --- a/tests/components/plex/test_config_flow.py +++ b/tests/components/plex/test_config_flow.py @@ -15,15 +15,13 @@ CONF_USE_EPISODE_ART, DOMAIN, PLEX_SERVER_CONFIG, - PLEX_UPDATE_PLATFORMS_SIGNAL, SERVERS, ) from homeassistant.config_entries import ENTRY_STATE_LOADED from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN, CONF_URL -from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.setup import async_setup_component -from .common import tick_debounce_timeout +from .common import trigger_plex_update from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN from .mock_classes import MockPlexAccount, MockPlexServer @@ -417,9 +415,7 @@ async def test_option_flow_new_users_available(hass, caplog): server_id = mock_plex_server.machineIdentifier - async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users diff --git a/tests/components/plex/test_init.py b/tests/components/plex/test_init.py index 8d6eed9e3b8a09..1aef7878df5e8e 100644 --- a/tests/components/plex/test_init.py +++ b/tests/components/plex/test_init.py @@ -23,11 +23,10 @@ CONF_URL, CONF_VERIFY_SSL, ) -from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from .common import tick_debounce_timeout +from .common import trigger_plex_update from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN from .mock_classes import MockPlexAccount, MockPlexServer @@ -75,7 +74,7 @@ async def test_setup_with_config(hass): ) -async def test_setup_with_config_entry(hass): +async def test_setup_with_config_entry(hass, caplog): """Test setup component with config.""" mock_plex_server = MockPlexServer() @@ -110,34 +109,31 @@ async def test_setup_with_config_entry(hass): hass.data[const.DOMAIN][const.PLATFORMS_COMPLETED][server_id] == const.PLATFORMS ) - async_dispatcher_send(hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) sensor = hass.states.get("sensor.plex_plex_server_1") assert sensor.state == str(len(mock_plex_server.accounts)) - async_dispatcher_send(hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) with patch.object( mock_plex_server, "clients", side_effect=plexapi.exceptions.BadRequest - ): - async_dispatcher_send( - hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id) - ) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + ) as patched_clients_bad_request: + await trigger_plex_update(hass, server_id) + + assert patched_clients_bad_request.called + assert "Error requesting Plex client data from server" in caplog.text with patch.object( mock_plex_server, "clients", side_effect=requests.exceptions.RequestException - ): - async_dispatcher_send( - hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id) - ) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + ) as patched_clients_requests_exception: + await trigger_plex_update(hass, server_id) + + assert patched_clients_requests_exception.called + assert ( + f"Could not connect to Plex server: {mock_plex_server.friendlyName}" + in caplog.text + ) async def test_set_config_entry_unique_id(hass): @@ -299,9 +295,7 @@ async def test_setup_with_photo_session(hass): server_id = mock_plex_server.machineIdentifier - async_dispatcher_send(hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) media_player = hass.states.get("media_player.plex_product_title") assert media_player.state == "idle" diff --git a/tests/components/plex/test_server.py b/tests/components/plex/test_server.py index 2f18bb1cf72f3a..242c0fe550463f 100644 --- a/tests/components/plex/test_server.py +++ b/tests/components/plex/test_server.py @@ -1,5 +1,6 @@ """Tests for Plex server.""" import copy +from datetime import timedelta from asynctest import patch @@ -7,17 +8,19 @@ from homeassistant.components.plex.const import ( CONF_IGNORE_NEW_SHARED_USERS, CONF_MONITORED_USERS, + DEBOUNCE_TIMEOUT, DOMAIN, PLEX_UPDATE_PLATFORMS_SIGNAL, SERVERS, ) from homeassistant.helpers.dispatcher import async_dispatcher_send +import homeassistant.util.dt as dt_util -from .common import tick_debounce_timeout +from .common import trigger_plex_update from .const import DEFAULT_DATA, DEFAULT_OPTIONS from .mock_classes import MockPlexServer -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed async def test_new_users_available(hass): @@ -45,9 +48,7 @@ async def test_new_users_available(hass): server_id = mock_plex_server.machineIdentifier - async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users @@ -85,9 +86,7 @@ async def test_new_ignored_users_available(hass, caplog): server_id = mock_plex_server.machineIdentifier - async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users @@ -121,9 +120,7 @@ async def test_mark_sessions_idle(hass): server_id = mock_plex_server.machineIdentifier - async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) sensor = hass.states.get("sensor.plex_plex_server_1") assert sensor.state == str(len(mock_plex_server.accounts)) @@ -131,9 +128,44 @@ async def test_mark_sessions_idle(hass): mock_plex_server.clear_clients() mock_plex_server.clear_sessions() - async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) - tick_debounce_timeout(hass) - await hass.async_block_till_done() + await trigger_plex_update(hass, server_id) sensor = hass.states.get("sensor.plex_plex_server_1") assert sensor.state == "0" + + +async def test_debouncer(hass, caplog): + """Test debouncer decorator logic.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=DEFAULT_DATA, + options=DEFAULT_OPTIONS, + unique_id=DEFAULT_DATA["server_id"], + ) + + mock_plex_server = MockPlexServer(config_entry=entry) + + with patch("plexapi.server.PlexServer", return_value=mock_plex_server), patch( + "homeassistant.components.plex.PlexWebsocket.listen" + ): + entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + server_id = mock_plex_server.machineIdentifier + + # First two updates are skipped + async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + await hass.async_block_till_done() + async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + await hass.async_block_till_done() + async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) + await hass.async_block_till_done() + + next_update = dt_util.utcnow() + timedelta(seconds=DEBOUNCE_TIMEOUT) + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + + assert ( + caplog.text.count(f"Throttling update of {mock_plex_server.friendlyName}") == 2 + ) From 280376cbda973ebf0374f249c2621919bc92fccc Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Fri, 3 Apr 2020 22:27:59 -0500 Subject: [PATCH 5/5] Use property instead --- homeassistant/components/plex/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/plex/server.py b/homeassistant/components/plex/server.py index a0b8b03670a9a8..f2a4908e119699 100644 --- a/homeassistant/components/plex/server.py +++ b/homeassistant/components/plex/server.py @@ -58,7 +58,7 @@ def wrapper(self): """Schedule async callback.""" nonlocal unsub if unsub: - _LOGGER.debug("Throttling update of %s", self._plex_server.friendlyName) + _LOGGER.debug("Throttling update of %s", self.friendly_name) unsub() # pylint: disable=not-callable unsub = async_call_later( self.hass, DEBOUNCE_TIMEOUT, partial(call_later_listener, self),