mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Repository event subscription (#67284)
This commit is contained in:
parent
73fdd47d54
commit
e65670fef4
@ -38,6 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
await coordinator.subscribe()
|
||||||
|
|
||||||
hass.data[DOMAIN][repository] = coordinator
|
hass.data[DOMAIN][repository] = coordinator
|
||||||
|
|
||||||
@ -45,7 +46,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
|
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -77,6 +77,10 @@ def async_cleanup_device_registry(
|
|||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
|
repositories: dict[str, GitHubDataUpdateCoordinator] = hass.data[DOMAIN]
|
||||||
|
for coordinator in repositories.values():
|
||||||
|
coordinator.unsubscribe()
|
||||||
|
|
||||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||||
hass.data.pop(DOMAIN)
|
hass.data.pop(DOMAIN)
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
@ -11,7 +11,18 @@ DOMAIN = "github"
|
|||||||
CLIENT_ID = "1440cafcc86e3ea5d6a2"
|
CLIENT_ID = "1440cafcc86e3ea5d6a2"
|
||||||
|
|
||||||
DEFAULT_REPOSITORIES = ["home-assistant/core", "esphome/esphome"]
|
DEFAULT_REPOSITORIES = ["home-assistant/core", "esphome/esphome"]
|
||||||
DEFAULT_UPDATE_INTERVAL = timedelta(seconds=300)
|
FALLBACK_UPDATE_INTERVAL = timedelta(hours=1, minutes=30)
|
||||||
|
|
||||||
CONF_ACCESS_TOKEN = "access_token"
|
CONF_ACCESS_TOKEN = "access_token"
|
||||||
CONF_REPOSITORIES = "repositories"
|
CONF_REPOSITORIES = "repositories"
|
||||||
|
|
||||||
|
|
||||||
|
REFRESH_EVENT_TYPES = (
|
||||||
|
"CreateEvent",
|
||||||
|
"ForkEvent",
|
||||||
|
"IssuesEvent",
|
||||||
|
"PullRequestEvent",
|
||||||
|
"PushEvent",
|
||||||
|
"ReleaseEvent",
|
||||||
|
"WatchEvent",
|
||||||
|
)
|
||||||
|
@ -6,15 +6,17 @@ from typing import Any
|
|||||||
from aiogithubapi import (
|
from aiogithubapi import (
|
||||||
GitHubAPI,
|
GitHubAPI,
|
||||||
GitHubConnectionException,
|
GitHubConnectionException,
|
||||||
|
GitHubEventModel,
|
||||||
GitHubException,
|
GitHubException,
|
||||||
GitHubRatelimitException,
|
GitHubRatelimitException,
|
||||||
GitHubResponseModel,
|
GitHubResponseModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import DEFAULT_UPDATE_INTERVAL, LOGGER
|
from .const import FALLBACK_UPDATE_INTERVAL, LOGGER, REFRESH_EVENT_TYPES
|
||||||
|
|
||||||
GRAPHQL_REPOSITORY_QUERY = """
|
GRAPHQL_REPOSITORY_QUERY = """
|
||||||
query ($owner: String!, $repository: String!) {
|
query ($owner: String!, $repository: String!) {
|
||||||
@ -109,13 +111,14 @@ class GitHubDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
self.repository = repository
|
self.repository = repository
|
||||||
self._client = client
|
self._client = client
|
||||||
self._last_response: GitHubResponseModel[dict[str, Any]] | None = None
|
self._last_response: GitHubResponseModel[dict[str, Any]] | None = None
|
||||||
|
self._subscription_id: str | None = None
|
||||||
self.data = {}
|
self.data = {}
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
name=repository,
|
name=repository,
|
||||||
update_interval=DEFAULT_UPDATE_INTERVAL,
|
update_interval=FALLBACK_UPDATE_INTERVAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_update_data(self) -> GitHubResponseModel[dict[str, Any]]:
|
async def _async_update_data(self) -> GitHubResponseModel[dict[str, Any]]:
|
||||||
@ -136,3 +139,26 @@ class GitHubDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
else:
|
else:
|
||||||
self._last_response = response
|
self._last_response = response
|
||||||
return response.data["data"]["repository"]
|
return response.data["data"]["repository"]
|
||||||
|
|
||||||
|
async def _handle_event(self, event: GitHubEventModel) -> None:
|
||||||
|
"""Handle an event."""
|
||||||
|
if event.type in REFRESH_EVENT_TYPES:
|
||||||
|
await self.async_request_refresh()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def _handle_error(error: GitHubException) -> None:
|
||||||
|
"""Handle an error."""
|
||||||
|
LOGGER.error("An error occurred while processing new events - %s", error)
|
||||||
|
|
||||||
|
async def subscribe(self) -> None:
|
||||||
|
"""Subscribe to repository events."""
|
||||||
|
self._subscription_id = await self._client.repos.events.subscribe(
|
||||||
|
self.repository,
|
||||||
|
event_callback=self._handle_event,
|
||||||
|
error_callback=self._handle_error,
|
||||||
|
)
|
||||||
|
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.unsubscribe)
|
||||||
|
|
||||||
|
def unsubscribe(self, *args) -> None:
|
||||||
|
"""Unsubscribe to repository events."""
|
||||||
|
self._client.repos.events.unsubscribe(subscription_id=self._subscription_id)
|
||||||
|
@ -31,6 +31,11 @@ async def setup_github_integration(
|
|||||||
},
|
},
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
aioclient_mock.get(
|
||||||
|
f"https://api.github.com/repos/{repository}/events",
|
||||||
|
json=[],
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
aioclient_mock.post(
|
aioclient_mock.post(
|
||||||
"https://api.github.com/graphql",
|
"https://api.github.com/graphql",
|
||||||
json=json.loads(load_fixture("graphql.json", DOMAIN)),
|
json=json.loads(load_fixture("graphql.json", DOMAIN)),
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
"""Test GitHub sensor."""
|
"""Test GitHub sensor."""
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from homeassistant.components.github.const import DEFAULT_UPDATE_INTERVAL, DOMAIN
|
from homeassistant.components.github.const import DOMAIN, FALLBACK_UPDATE_INTERVAL
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.util import dt
|
from homeassistant.util import dt
|
||||||
|
|
||||||
|
from .common import TEST_REPOSITORY
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
|
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
|
||||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||||
|
|
||||||
@ -22,15 +24,21 @@ async def test_sensor_updates_with_empty_release_array(
|
|||||||
|
|
||||||
response_json = json.loads(load_fixture("graphql.json", DOMAIN))
|
response_json = json.loads(load_fixture("graphql.json", DOMAIN))
|
||||||
response_json["data"]["repository"]["release"] = None
|
response_json["data"]["repository"]["release"] = None
|
||||||
|
headers = json.loads(load_fixture("base_headers.json", DOMAIN))
|
||||||
|
|
||||||
aioclient_mock.clear_requests()
|
aioclient_mock.clear_requests()
|
||||||
|
aioclient_mock.get(
|
||||||
|
f"https://api.github.com/repos/{TEST_REPOSITORY}/events",
|
||||||
|
json=[],
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
aioclient_mock.post(
|
aioclient_mock.post(
|
||||||
"https://api.github.com/graphql",
|
"https://api.github.com/graphql",
|
||||||
json=response_json,
|
json=response_json,
|
||||||
headers=json.loads(load_fixture("base_headers.json", DOMAIN)),
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
async_fire_time_changed(hass, dt.utcnow() + DEFAULT_UPDATE_INTERVAL)
|
async_fire_time_changed(hass, dt.utcnow() + FALLBACK_UPDATE_INTERVAL)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
new_state = hass.states.get(TEST_SENSOR_ENTITY)
|
new_state = hass.states.get(TEST_SENSOR_ENTITY)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user