165 lines
4.5 KiB
Python
165 lines
4.5 KiB
Python
"""Custom data update coordinator for the GitHub integration."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from aiogithubapi import (
|
|
GitHubAPI,
|
|
GitHubConnectionException,
|
|
GitHubEventModel,
|
|
GitHubException,
|
|
GitHubRatelimitException,
|
|
GitHubResponseModel,
|
|
)
|
|
|
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
|
|
|
from .const import FALLBACK_UPDATE_INTERVAL, LOGGER, REFRESH_EVENT_TYPES
|
|
|
|
GRAPHQL_REPOSITORY_QUERY = """
|
|
query ($owner: String!, $repository: String!) {
|
|
rateLimit {
|
|
cost
|
|
remaining
|
|
}
|
|
repository(owner: $owner, name: $repository) {
|
|
default_branch_ref: defaultBranchRef {
|
|
commit: target {
|
|
... on Commit {
|
|
message: messageHeadline
|
|
url
|
|
sha: oid
|
|
}
|
|
}
|
|
}
|
|
stargazers_count: stargazerCount
|
|
forks_count: forkCount
|
|
full_name: nameWithOwner
|
|
id: databaseId
|
|
watchers(first: 1) {
|
|
total: totalCount
|
|
}
|
|
discussion: discussions(
|
|
first: 1
|
|
orderBy: {field: CREATED_AT, direction: DESC}
|
|
) {
|
|
total: totalCount
|
|
discussions: nodes {
|
|
title
|
|
url
|
|
number
|
|
}
|
|
}
|
|
issue: issues(
|
|
first: 1
|
|
states: OPEN
|
|
orderBy: {field: CREATED_AT, direction: DESC}
|
|
) {
|
|
total: totalCount
|
|
issues: nodes {
|
|
title
|
|
url
|
|
number
|
|
}
|
|
}
|
|
pull_request: pullRequests(
|
|
first: 1
|
|
states: OPEN
|
|
orderBy: {field: CREATED_AT, direction: DESC}
|
|
) {
|
|
total: totalCount
|
|
pull_requests: nodes {
|
|
title
|
|
url
|
|
number
|
|
}
|
|
}
|
|
release: latestRelease {
|
|
name
|
|
url
|
|
tag: tagName
|
|
}
|
|
refs(
|
|
first: 1
|
|
refPrefix: "refs/tags/"
|
|
orderBy: {field: TAG_COMMIT_DATE, direction: DESC}
|
|
) {
|
|
tags: nodes {
|
|
name
|
|
target {
|
|
url: commitUrl
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
|
|
class GitHubDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|
"""Data update coordinator for the GitHub integration."""
|
|
|
|
def __init__(
|
|
self,
|
|
hass: HomeAssistant,
|
|
client: GitHubAPI,
|
|
repository: str,
|
|
) -> None:
|
|
"""Initialize GitHub data update coordinator base class."""
|
|
self.repository = repository
|
|
self._client = client
|
|
self._last_response: GitHubResponseModel[dict[str, Any]] | None = None
|
|
self._subscription_id: str | None = None
|
|
self.data = {}
|
|
|
|
super().__init__(
|
|
hass,
|
|
LOGGER,
|
|
name=repository,
|
|
update_interval=FALLBACK_UPDATE_INTERVAL,
|
|
)
|
|
|
|
async def _async_update_data(self) -> GitHubResponseModel[dict[str, Any]]:
|
|
"""Update data."""
|
|
owner, repository = self.repository.split("/")
|
|
try:
|
|
response = await self._client.graphql(
|
|
query=GRAPHQL_REPOSITORY_QUERY,
|
|
variables={"owner": owner, "repository": repository},
|
|
)
|
|
except (GitHubConnectionException, GitHubRatelimitException) as exception:
|
|
# These are expected and we dont log anything extra
|
|
raise UpdateFailed(exception) from exception
|
|
except GitHubException as exception:
|
|
# These are unexpected and we log the trace to help with troubleshooting
|
|
LOGGER.exception(exception)
|
|
raise UpdateFailed(exception) from exception
|
|
|
|
self._last_response = response
|
|
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)
|