From 4a4c07ac1b209471ebb3467c7a884c29e2f5edd6 Mon Sep 17 00:00:00 2001 From: Daniel Winks Date: Tue, 25 Sep 2018 05:18:23 -0400 Subject: [PATCH] GitLab-CI sensor integration addition. (#16561) * Updates to GitLab Sensor * Updates to GitLab_CI sensor. * Added GitLab_CI sensor. * Updated interval to a more appropriate 300 seconds. * Added GitLab_CI.py to ommitted files. * Initial refactor to use python-gitlab PyPI module. * Fixes to dict parsing. * Updated required packages for GitLab_CI requirements. * Updates and refactoring to more closely align with Home-Assistant standards. * Moved import to init, removed unreachable requests exception. * Removed references to STATE_UNKNOWN and replaced with None * Removed extra whitespace * Changed PLATFORM_SCHEMA and renamed add_devices. Changed PLATFORM_SCHEMA to use SCAN_INTERVAL and add_devices to add_entities * Added configurable name, changed logger and removed cruft. * Removed _status to use _state instead, as both held same value. * Fixed ATTR_BUILD_BRANCH, removed more extraneous cruft. * Changed required config keys to dict[key] format. * Removed extraneous CONF_SCAN_INTERVAL as it's already defined. --- .coveragerc | 1 + homeassistant/components/sensor/gitlab_ci.py | 174 +++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 178 insertions(+) create mode 100644 homeassistant/components/sensor/gitlab_ci.py diff --git a/.coveragerc b/.coveragerc index a8d7d89544d2b..defbc0022bd15 100644 --- a/.coveragerc +++ b/.coveragerc @@ -679,6 +679,7 @@ omit = homeassistant/components/sensor/fritzbox_netmonitor.py homeassistant/components/sensor/gearbest.py homeassistant/components/sensor/geizhals.py + homeassistant/components/sensor/gitlab_ci.py homeassistant/components/sensor/gitter.py homeassistant/components/sensor/glances.py homeassistant/components/sensor/google_travel_time.py diff --git a/homeassistant/components/sensor/gitlab_ci.py b/homeassistant/components/sensor/gitlab_ci.py new file mode 100644 index 0000000000000..ceb5f75cacec5 --- /dev/null +++ b/homeassistant/components/sensor/gitlab_ci.py @@ -0,0 +1,174 @@ +"""Module for retrieving latest GitLab CI job information.""" +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_NAME, CONF_SCAN_INTERVAL, CONF_TOKEN, CONF_URL) +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +CONF_GITLAB_ID = 'gitlab_id' +CONF_ATTRIBUTION = "Information provided by https://gitlab.com/" + +ICON_HAPPY = 'mdi:emoticon-happy' +ICON_SAD = 'mdi:emoticon-happy' +ICON_OTHER = 'mdi:git' + +ATTR_BUILD_ID = 'build id' +ATTR_BUILD_STATUS = 'build_status' +ATTR_BUILD_STARTED = 'build_started' +ATTR_BUILD_FINISHED = 'build_finished' +ATTR_BUILD_DURATION = 'build_duration' +ATTR_BUILD_COMMIT_ID = 'commit id' +ATTR_BUILD_COMMIT_DATE = 'commit date' +ATTR_BUILD_BRANCH = 'build branch' + +SCAN_INTERVAL = timedelta(seconds=300) + +_LOGGER = logging.getLogger(__name__) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_TOKEN): cv.string, + vol.Required(CONF_GITLAB_ID): cv.string, + vol.Optional(CONF_NAME, default='GitLab CI Status'): cv.string, + vol.Optional(CONF_URL, default='https://gitlab.com'): cv.string +}) + +REQUIREMENTS = ['python-gitlab==1.6.0'] + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Sensor platform setup.""" + _name = config.get(CONF_NAME) + _interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL) + _url = config.get(CONF_URL) + + _gitlab_data = GitLabData( + priv_token=config[CONF_TOKEN], + gitlab_id=config[CONF_GITLAB_ID], + interval=_interval, + url=_url + ) + + add_entities([GitLabSensor(_gitlab_data, _name)], True) + + +class GitLabSensor(Entity): + """Representation of a Sensor.""" + + def __init__(self, gitlab_data, name): + """Initialize the sensor.""" + self._available = False + self._state = None + self._started_at = None + self._finished_at = None + self._duration = None + self._commit_id = None + self._commit_date = None + self._build_id = None + self._branch = None + self._gitlab_data = gitlab_data + self._name = name + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @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_ATTRIBUTION: CONF_ATTRIBUTION, + ATTR_BUILD_STATUS: self._state, + ATTR_BUILD_STARTED: self._started_at, + ATTR_BUILD_FINISHED: self._finished_at, + ATTR_BUILD_DURATION: self._duration, + ATTR_BUILD_COMMIT_ID: self._commit_id, + ATTR_BUILD_COMMIT_DATE: self._commit_date, + ATTR_BUILD_ID: self._build_id, + ATTR_BUILD_BRANCH: self._branch + } + + @property + def icon(self): + """Return the icon to use in the frontend.""" + if self._state == 'success': + return ICON_HAPPY + if self._state == 'failed': + return ICON_SAD + return ICON_OTHER + + def update(self): + """Collect updated data from GitLab API.""" + self._gitlab_data.update() + + self._state = self._gitlab_data.status + self._started_at = self._gitlab_data.started_at + self._finished_at = self._gitlab_data.finished_at + self._duration = self._gitlab_data.duration + self._commit_id = self._gitlab_data.commit_id + self._commit_date = self._gitlab_data.commit_date + self._build_id = self._gitlab_data.build_id + self._branch = self._gitlab_data.branch + self._available = self._gitlab_data.available + + +class GitLabData(): + """GitLab Data object.""" + + def __init__(self, gitlab_id, priv_token, interval, url): + """Fetch data from GitLab API for most recent CI job.""" + import gitlab + self._gitlab_id = gitlab_id + self._gitlab = gitlab.Gitlab( + url, private_token=priv_token, per_page=1) + self._gitlab.auth() + self._gitlab_exceptions = gitlab.exceptions + self.update = Throttle(interval)(self._update) + + self.available = False + self.status = None + self.started_at = None + self.finished_at = None + self.duration = None + self.commit_id = None + self.commit_date = None + self.build_id = None + self.branch = None + + def _update(self): + try: + _projects = self._gitlab.projects.get(self._gitlab_id) + _last_pipeline = _projects.pipelines.list(page=1)[0] + _last_job = _last_pipeline.jobs.list(page=1)[0] + self.status = _last_pipeline.attributes.get('status') + self.started_at = _last_job.attributes.get('started_at') + self.finished_at = _last_job.attributes.get('finished_at') + self.duration = _last_job.attributes.get('duration') + _commit = _last_job.attributes.get('commit') + self.commit_id = _commit.get('id') + self.commit_date = _commit.get('committed_date') + self.build_id = _last_job.attributes.get('id') + self.branch = _last_job.attributes.get('ref') + self.available = True + except self._gitlab_exceptions.GitlabAuthenticationError as erra: + _LOGGER.error("Authentication Error: %s", erra) + self.available = False + except self._gitlab_exceptions.GitlabGetError as errg: + _LOGGER.error("Project Not Found: %s", errg) + self.available = False diff --git a/requirements_all.txt b/requirements_all.txt index cfb17e64a7a7d..40d4442dee2a0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1114,6 +1114,9 @@ python-forecastio==1.4.0 # homeassistant.components.gc100 python-gc100==1.0.3a +# homeassistant.components.sensor.gitlab_ci +python-gitlab==1.6.0 + # homeassistant.components.sensor.hp_ilo python-hpilo==3.9