From fe7134b897625a9c45764ab13c2f4c092ac1a61a Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 14 Sep 2015 14:08:30 +0200 Subject: [PATCH] Add glances sensor --- homeassistant/components/sensor/glances.py | 204 +++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 homeassistant/components/sensor/glances.py diff --git a/homeassistant/components/sensor/glances.py b/homeassistant/components/sensor/glances.py new file mode 100644 index 00000000000..0a334f2c610 --- /dev/null +++ b/homeassistant/components/sensor/glances.py @@ -0,0 +1,204 @@ +""" +homeassistant.components.sensor.glances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Gathers system information of hosts which running glances. + + +Configuration: + +To use the glances sensor you will need to add something like the following +to your configuration.yaml file. + +sensor: + platform: glances + name: Glances sensor + host: IP_ADDRESS + port: 61208 + resources: + - 'disk_use_percent' + - 'disk_use' + - 'disk_free' + - 'memory_use_percent' + - 'memory_use' + - 'memory_free' + - 'swap_use_percent' + - 'swap_use' + - 'swap_free' + - 'processor_load' + - 'process_running' + - 'process_total' + - 'process_thread' + - 'process_sleeping' + +Variables: + +name +*Optional +The name of the sensor. Default is 'Glances Sensor'. + +host +*Required +The IP address of your host, e.g. 192.168.1.32. + +port +*Optional +The network port to connect to. Default is 61208. + +resources +*Required +Resources to monitor on the host. See the configuration example above for a +list of all available conditions to monitor. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.glances.html +""" +import logging +import requests +from datetime import timedelta + +from homeassistant.util import Throttle +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Glances Sensor' +_RESOURCE = '/api/2/all' +SENSOR_TYPES = { + 'disk_use_percent': ['Disk Use', '%'], + 'disk_use': ['Disk Use', 'GiB'], + 'disk_free': ['Disk Free', 'GiB'], + 'memory_use_percent': ['RAM Use', '%'], + 'memory_use': ['RAM Use', 'MiB'], + 'memory_free': ['RAM Free', 'MiB'], + 'swap_use_percent': ['Swap Use', '%'], + 'swap_use': ['Swap Use', 'GiB'], + 'swap_free': ['Swap Free', 'GiB'], + 'processor_load': ['CPU Load', ''], + 'process_running': ['Running', ''], + 'process_total': ['Total', ''], + 'process_thread': ['Thread', ''], + 'process_sleeping': ['Sleeping', ''] +} + +_LOGGER = logging.getLogger(__name__) +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + + +# pylint: disable=unused-variable +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Setup the Glances sensor. """ + + if not config.get('host'): + _LOGGER.error('"host:" is missing your configuration') + return False + + host = config.get('host') + port = config.get('port', 61208) + url = 'http://{}:{}{}'.format(host, port, _RESOURCE) + + try: + response = requests.get(url, timeout=10) + if not response.ok: + _LOGGER.error('Response status is "%s"', response.status_code) + except requests.exceptions.MissingSchema: + _LOGGER.error('Missing resource or schema in configuration. ' + 'Please heck our details in the configuration file.') + return False + except requests.exceptions.ConnectionError: + _LOGGER.error('No route to resource/endpoint. ' + 'Please check the details in the configuration file.') + return False + + rest = GlancesData(url) + + dev = [] + for resource in config['resources']: + if resource not in SENSOR_TYPES: + _LOGGER.error('Sensor type: "%s" does not exist', resource) + else: + dev.append(GlancesSensor(rest, resource)) + + add_devices(dev) + + +class GlancesSensor(Entity): + """ Implements a REST sensor. """ + + def __init__(self, rest, sensor_type): + self.rest = rest + self._name = SENSOR_TYPES[sensor_type][0] + self.type = sensor_type + self._state = None + self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] + self.update() + + @property + def name(self): + """ The name of the sensor. """ + return self._name + + @property + def unit_of_measurement(self): + """ Unit the value is expressed in. """ + return self._unit_of_measurement + + @property + def state(self): + """ Returns the state of the device. """ + return self._state + + # pylint: disable=too-many-branches + def update(self): + """ Gets the latest data from REST API and updates the state. """ + self.rest.update() + value = self.rest.data + + if value is not None: + if self.type == 'disk_use_percent': + self._state = value['fs'][0]['percent'] + elif self.type == 'disk_use': + self._state = round(value['fs'][0]['used'] / 1024**3, 1) + elif self.type == 'disk_free': + self._state = round(value['fs'][0]['free'] / 1024**3, 1) + elif self.type == 'memory_use_percent': + self._state = value['mem']['percent'] + elif self.type == 'memory_use': + self._state = round(value['mem']['used'] / 1024**2, 1) + elif self.type == 'memory_free': + self._state = round(value['mem']['free'] / 1024**2, 1) + elif self.type == 'swap_use_percent': + self._state = value['memswap']['percent'] + elif self.type == 'swap_use': + self._state = round(value['memswap']['used'] / 1024**3, 1) + elif self.type == 'swap_free': + self._state = round(value['memswap']['free'] / 1024**3, 1) + elif self.type == 'processor_load': + self._state = value['load']['min15'] + elif self.type == 'process_running': + self._state = value['processcount']['running'] + elif self.type == 'process_total': + self._state = value['processcount']['total'] + elif self.type == 'process_thread': + self._state = value['processcount']['thread'] + elif self.type == 'process_sleeping': + self._state = value['processcount']['sleeping'] + + +# pylint: disable=too-few-public-methods +class GlancesData(object): + """ Class for handling the data retrieval. """ + + def __init__(self, resource): + self.resource = resource + self.data = dict() + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """ Gets the latest data from REST service. """ + try: + response = requests.get(self.resource, timeout=10) + self.data = response.json() + except requests.exceptions.ConnectionError: + _LOGGER.error("No route to host/endpoint.") + self.data = None