"""Support for GitHub.""" from datetime import timedelta import logging import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( ATTR_NAME, CONF_ACCESS_TOKEN, CONF_NAME, CONF_PATH, CONF_URL, ) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) CONF_REPOS = "repositories" ATTR_LATEST_COMMIT_MESSAGE = "latest_commit_message" ATTR_LATEST_COMMIT_SHA = "latest_commit_sha" ATTR_LATEST_RELEASE_URL = "latest_release_url" ATTR_LATEST_OPEN_ISSUE_URL = "latest_open_issue_url" ATTR_OPEN_ISSUES = "open_issues" ATTR_LATEST_OPEN_PULL_REQUEST_URL = "latest_open_pull_request_url" ATTR_OPEN_PULL_REQUESTS = "open_pull_requests" ATTR_PATH = "path" ATTR_STARGAZERS = "stargazers" DEFAULT_NAME = "GitHub" SCAN_INTERVAL = timedelta(seconds=300) REPO_SCHEMA = vol.Schema( {vol.Required(CONF_PATH): cv.string, vol.Optional(CONF_NAME): cv.string} ) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_ACCESS_TOKEN): cv.string, vol.Optional(CONF_URL): cv.url, vol.Required(CONF_REPOS): vol.All(cv.ensure_list, [REPO_SCHEMA]), } ) def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the GitHub sensor platform.""" sensors = [] for repository in config[CONF_REPOS]: data = GitHubData( repository=repository, access_token=config.get(CONF_ACCESS_TOKEN), server_url=config.get(CONF_URL), ) if data.setup_error is True: _LOGGER.error( "Error setting up GitHub platform. %s", "Check previous errors for details", ) return sensors.append(GitHubSensor(data)) add_entities(sensors, True) class GitHubSensor(Entity): """Representation of a GitHub sensor.""" def __init__(self, github_data): """Initialize the GitHub sensor.""" self._unique_id = github_data.repository_path self._name = None self._state = None self._available = False self._repository_path = None self._latest_commit_message = None self._latest_commit_sha = None self._latest_release_url = None self._open_issue_count = None self._latest_open_issue_url = None self._pull_request_count = None self._latest_open_pr_url = None self._stargazers = None self._github_data = github_data @property def name(self): """Return the name of the sensor.""" return self._name @property def unique_id(self): """Return unique ID for the sensor.""" return self._unique_id @property def state(self): """Return the state of the sensor.""" return self._state @property def available(self): """Return True if entity is available.""" return self._available @property def device_state_attributes(self): """Return the state attributes.""" return { ATTR_PATH: self._repository_path, ATTR_NAME: self._name, ATTR_LATEST_COMMIT_MESSAGE: self._latest_commit_message, ATTR_LATEST_COMMIT_SHA: self._latest_commit_sha, ATTR_LATEST_RELEASE_URL: self._latest_release_url, ATTR_LATEST_OPEN_ISSUE_URL: self._latest_open_issue_url, ATTR_OPEN_ISSUES: self._open_issue_count, ATTR_LATEST_OPEN_PULL_REQUEST_URL: self._latest_open_pr_url, ATTR_OPEN_PULL_REQUESTS: self._pull_request_count, ATTR_STARGAZERS: self._stargazers, } @property def icon(self): """Return the icon to use in the frontend.""" return "mdi:github-circle" def update(self): """Collect updated data from GitHub API.""" self._github_data.update() self._name = self._github_data.name self._state = self._github_data.latest_commit_sha self._repository_path = self._github_data.repository_path self._available = self._github_data.available self._latest_commit_message = self._github_data.latest_commit_message self._latest_commit_sha = self._github_data.latest_commit_sha self._latest_release_url = self._github_data.latest_release_url self._open_issue_count = self._github_data.open_issue_count self._latest_open_issue_url = self._github_data.latest_open_issue_url self._pull_request_count = self._github_data.pull_request_count self._latest_open_pr_url = self._github_data.latest_open_pr_url self._stargazers = self._github_data.stargazers class GitHubData: """GitHub Data object.""" def __init__(self, repository, access_token=None, server_url=None): """Set up GitHub.""" import github self._github = github self.setup_error = False try: if server_url is not None: server_url += "/api/v3" self._github_obj = github.Github(access_token, base_url=server_url) else: self._github_obj = github.Github(access_token) self.repository_path = repository[CONF_PATH] repo = self._github_obj.get_repo(self.repository_path) except self._github.GithubException as err: _LOGGER.error("GitHub error for %s: %s", self.repository_path, err) self.setup_error = True return self.name = repository.get(CONF_NAME, repo.name) self.available = False self.latest_commit_message = None self.latest_commit_sha = None self.latest_release_url = None self.open_issue_count = None self.latest_open_issue_url = None self.pull_request_count = None self.latest_open_pr_url = None self.stargazers = None def update(self): """Update GitHub Sensor.""" try: repo = self._github_obj.get_repo(self.repository_path) self.stargazers = repo.stargazers_count open_issues = repo.get_issues(state="open", sort="created") if open_issues is not None: self.open_issue_count = open_issues.totalCount if open_issues.totalCount > 0: self.latest_open_issue_url = open_issues[0].html_url open_pull_requests = repo.get_pulls(state="open", sort="created") if open_pull_requests is not None: self.pull_request_count = open_pull_requests.totalCount if open_pull_requests.totalCount > 0: self.latest_open_pr_url = open_pull_requests[0].html_url latest_commit = repo.get_commits()[0] self.latest_commit_sha = latest_commit.sha self.latest_commit_message = latest_commit.commit.message releases = repo.get_releases() if releases and releases.totalCount > 0: self.latest_release_url = releases[0].html_url self.available = True except self._github.GithubException as err: _LOGGER.error("GitHub error for %s: %s", self.repository_path, err) self.available = False