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
This commit is contained in:
Alex Wijnholds 2024-09-03 17:44:20 +02:00 committed by GitHub
parent 8255728f53
commit 00533bae4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 61 additions and 1 deletions

View file

@ -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"

View file

@ -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

View file

@ -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,
),
]

View file

@ -46,7 +46,8 @@
"published_at": { "name": "Published at" }
}
},
"subscribers": { "name": "Subscribers" }
"subscribers": { "name": "Subscribers" },
"views": { "name": "Views" }
}
}
}

View file

@ -12,6 +12,7 @@
}),
'subscriber_count': 2290000,
'title': 'Google for Developers',
'total_views': 214141263,
}),
})
# ---

View file

@ -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': <ANY>,
'entity_id': 'sensor.google_for_developers_views',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'entity_id': 'sensor.google_for_developers_views',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '214141263',
})
# ---

View file

@ -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"