From 00533bae4ba42be8ad33c50fb8eadcd982af0be5 Mon Sep 17 00:00:00 2001 From: Alex Wijnholds <1023654+Alexwijn@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:44:20 +0200 Subject: [PATCH] Add support for total YouTube views (#123144) * Add support for retrieving the total views of a channel. * Add missing tests * Re-order imports * Another run on code format * Add missing translation * Update YouTube test snapshots --- homeassistant/components/youtube/const.py | 1 + .../components/youtube/coordinator.py | 2 ++ homeassistant/components/youtube/sensor.py | 10 +++++++ homeassistant/components/youtube/strings.json | 3 +- .../youtube/snapshots/test_diagnostics.ambr | 1 + .../youtube/snapshots/test_sensor.ambr | 30 +++++++++++++++++++ tests/components/youtube/test_sensor.py | 15 ++++++++++ 7 files changed, 61 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/youtube/const.py b/homeassistant/components/youtube/const.py index a663c487d0a..da5a554f364 100644 --- a/homeassistant/components/youtube/const.py +++ b/homeassistant/components/youtube/const.py @@ -15,6 +15,7 @@ AUTH = "auth" LOGGER = logging.getLogger(__package__) ATTR_TITLE = "title" +ATTR_TOTAL_VIEWS = "total_views" ATTR_LATEST_VIDEO = "latest_video" ATTR_SUBSCRIBER_COUNT = "subscriber_count" ATTR_DESCRIPTION = "description" diff --git a/homeassistant/components/youtube/coordinator.py b/homeassistant/components/youtube/coordinator.py index 4599342c84d..0da480f1169 100644 --- a/homeassistant/components/youtube/coordinator.py +++ b/homeassistant/components/youtube/coordinator.py @@ -22,6 +22,7 @@ from .const import ( ATTR_SUBSCRIBER_COUNT, ATTR_THUMBNAIL, ATTR_TITLE, + ATTR_TOTAL_VIEWS, ATTR_VIDEO_ID, CONF_CHANNELS, DOMAIN, @@ -68,6 +69,7 @@ class YouTubeDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): ATTR_ICON: channel.snippet.thumbnails.get_highest_quality().url, ATTR_LATEST_VIDEO: latest_video, ATTR_SUBSCRIBER_COUNT: channel.statistics.subscriber_count, + ATTR_TOTAL_VIEWS: channel.statistics.view_count, } except UnauthorizedError as err: raise ConfigEntryAuthFailed from err diff --git a/homeassistant/components/youtube/sensor.py b/homeassistant/components/youtube/sensor.py index bc69f92e8fd..8832382508c 100644 --- a/homeassistant/components/youtube/sensor.py +++ b/homeassistant/components/youtube/sensor.py @@ -20,6 +20,7 @@ from .const import ( ATTR_SUBSCRIBER_COUNT, ATTR_THUMBNAIL, ATTR_TITLE, + ATTR_TOTAL_VIEWS, ATTR_VIDEO_ID, COORDINATOR, DOMAIN, @@ -58,6 +59,15 @@ SENSOR_TYPES = [ entity_picture_fn=lambda channel: channel[ATTR_ICON], attributes_fn=None, ), + YouTubeSensorEntityDescription( + key="views", + translation_key="views", + native_unit_of_measurement="views", + available_fn=lambda _: True, + value_fn=lambda channel: channel[ATTR_TOTAL_VIEWS], + entity_picture_fn=lambda channel: channel[ATTR_ICON], + attributes_fn=None, + ), ] diff --git a/homeassistant/components/youtube/strings.json b/homeassistant/components/youtube/strings.json index d664e2f15e7..5902d3a4482 100644 --- a/homeassistant/components/youtube/strings.json +++ b/homeassistant/components/youtube/strings.json @@ -46,7 +46,8 @@ "published_at": { "name": "Published at" } } }, - "subscribers": { "name": "Subscribers" } + "subscribers": { "name": "Subscribers" }, + "views": { "name": "Views" } } } } diff --git a/tests/components/youtube/snapshots/test_diagnostics.ambr b/tests/components/youtube/snapshots/test_diagnostics.ambr index a938cb8daad..50dc2757e8c 100644 --- a/tests/components/youtube/snapshots/test_diagnostics.ambr +++ b/tests/components/youtube/snapshots/test_diagnostics.ambr @@ -12,6 +12,7 @@ }), 'subscriber_count': 2290000, 'title': 'Google for Developers', + 'total_views': 214141263, }), }) # --- diff --git a/tests/components/youtube/snapshots/test_sensor.ambr b/tests/components/youtube/snapshots/test_sensor.ambr index cddfa6f6a3d..dce546b4803 100644 --- a/tests/components/youtube/snapshots/test_sensor.ambr +++ b/tests/components/youtube/snapshots/test_sensor.ambr @@ -30,6 +30,21 @@ 'state': '2290000', }) # --- +# name: test_sensor.2 + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'entity_picture': 'https://yt3.ggpht.com/fca_HuJ99xUxflWdex0XViC3NfctBFreIl8y4i9z411asnGTWY-Ql3MeH_ybA4kNaOjY7kyA=s800-c-k-c0x00ffffff-no-rj', + 'friendly_name': 'Google for Developers Views', + 'unit_of_measurement': 'views', + }), + 'context': , + 'entity_id': 'sensor.google_for_developers_views', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '214141263', + }) +# --- # name: test_sensor_without_uploaded_video StateSnapshot({ 'attributes': ReadOnlyDict({ @@ -58,3 +73,18 @@ 'state': '2290000', }) # --- +# name: test_sensor_without_uploaded_video.2 + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'entity_picture': 'https://yt3.ggpht.com/fca_HuJ99xUxflWdex0XViC3NfctBFreIl8y4i9z411asnGTWY-Ql3MeH_ybA4kNaOjY7kyA=s800-c-k-c0x00ffffff-no-rj', + 'friendly_name': 'Google for Developers Views', + 'unit_of_measurement': 'views', + }), + 'context': , + 'entity_id': 'sensor.google_for_developers_views', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '214141263', + }) +# --- diff --git a/tests/components/youtube/test_sensor.py b/tests/components/youtube/test_sensor.py index ae0c38306e4..e883347c8db 100644 --- a/tests/components/youtube/test_sensor.py +++ b/tests/components/youtube/test_sensor.py @@ -29,6 +29,9 @@ async def test_sensor( state = hass.states.get("sensor.google_for_developers_subscribers") assert state == snapshot + state = hass.states.get("sensor.google_for_developers_views") + assert state == snapshot + async def test_sensor_without_uploaded_video( hass: HomeAssistant, snapshot: SnapshotAssertion, setup_integration: ComponentSetup @@ -52,6 +55,9 @@ async def test_sensor_without_uploaded_video( state = hass.states.get("sensor.google_for_developers_subscribers") assert state == snapshot + state = hass.states.get("sensor.google_for_developers_views") + assert state == snapshot + async def test_sensor_updating( hass: HomeAssistant, setup_integration: ComponentSetup @@ -95,6 +101,9 @@ async def test_sensor_reauth_trigger( state = hass.states.get("sensor.google_for_developers_subscribers") assert state.state == "2290000" + state = hass.states.get("sensor.google_for_developers_views") + assert state.state == "214141263" + mock.set_thrown_exception(UnauthorizedError()) future = dt_util.utcnow() + timedelta(minutes=15) async_fire_time_changed(hass, future) @@ -121,6 +130,9 @@ async def test_sensor_unavailable( state = hass.states.get("sensor.google_for_developers_subscribers") assert state.state == "2290000" + state = hass.states.get("sensor.google_for_developers_views") + assert state.state == "214141263" + mock.set_thrown_exception(YouTubeBackendError()) future = dt_util.utcnow() + timedelta(minutes=15) async_fire_time_changed(hass, future) @@ -131,3 +143,6 @@ async def test_sensor_unavailable( state = hass.states.get("sensor.google_for_developers_subscribers") assert state.state == "unavailable" + + state = hass.states.get("sensor.google_for_developers_views") + assert state.state == "unavailable"