From 76ac913fc0862421b7e4ef1f32994c8084a21f86 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 21 Nov 2015 19:01:47 +0100 Subject: [PATCH 1/5] Add influx component --- homeassistant/components/influx.py | 143 +++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 homeassistant/components/influx.py diff --git a/homeassistant/components/influx.py b/homeassistant/components/influx.py new file mode 100644 index 00000000000..ef28308d77e --- /dev/null +++ b/homeassistant/components/influx.py @@ -0,0 +1,143 @@ +""" +homeassistant.components.influx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +InfluxDB component which allows you to send data to an Influx database. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/influx/ + +Configuration: + +influx: + host: localhost + port: 8086 + dbname: home_assistant + dbuser: DB_USER + dbuser_password: DB_USER_PASSWORD +""" +import logging +import requests +import socket + +import homeassistant.util as util +from homeassistant.helpers import validate_config +from homeassistant.const import (MATCH_ALL) + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "influx" +DEPENDENCIES = ['recorder'] + +INFLUX_CLIENT = None + +DEFAULT_HOST = 'localhost' +DEFAULT_PORT = 8086 +DEFAULT_DATABASE = 'home_assistant' + +REQUIREMENTS = ['influxdb==2.10.0'] + +CONF_HOST = 'host' +CONF_PORT = 'port' +CONF_DB_NAME = 'database' +CONF_USERNAME = 'username' +CONF_PASSWORD = 'password' + + +def setup(hass, config): + """ Setup the Influx component. """ + + from influxdb import exceptions + + if not validate_config(config, {DOMAIN: ['host']}, _LOGGER): + return False + + conf = config[DOMAIN] + + host = conf[CONF_HOST] + port = util.convert(conf.get(CONF_PORT), int, DEFAULT_PORT) + dbname = util.convert(conf.get(CONF_DB_NAME), str, DEFAULT_DATABASE) + username = util.convert(conf.get(CONF_USERNAME), str) + password = util.convert(conf.get(CONF_PASSWORD), str) + + global INFLUX_CLIENT + + try: + INFLUX_CLIENT = Influx(host, port, username, password, dbname) + except (socket.gaierror, requests.exceptions.ConnectionError): + _LOGGER.error("Database is not accessible. " + "Please check your entries in the configuration file.") + return False + + try: + INFLUX_CLIENT.create_database(dbname) + except exceptions.InfluxDBClientError: + _LOGGER.info("Database '%s' already exists", dbname) + + INFLUX_CLIENT.switch_user(username, password) + INFLUX_CLIENT.switch_database(dbname) + + def event_listener(event): + """ Listen for new messages on the bus and sends them to Influx. """ + event_data = event.as_dict() + json_body = [] + + if event_data['event_type'] is not 'time_changed': + try: + entity_id = event_data['data']['entity_id'] + new_state = event_data['data']['new_state'] + + json_body = [ + { + "measurement": entity_id.split('.')[1], + "tags": { + "type": entity_id.split('.')[0], + }, + "time": event_data['time_fired'], + "fields": { + "value": new_state.state + } + } + ] + except KeyError: + pass + + if json_body: + INFLUX_CLIENT.write_data(json_body) + + hass.bus.listen(MATCH_ALL, event_listener) + + return True + + +# pylint: disable=too-many-arguments +class Influx(object): + """ Implements the handling of an connection to an Influx database.. """ + + def __init__(self, host, port, username, password, dbname): + + from influxdb import InfluxDBClient + + self._host = host + self._port = port + self._username = username + self._password = password + self._dbname = dbname + + self.client = InfluxDBClient(self._host, self._port, self._username, + self._password, self._dbname) + + def switch_user(self, username, password): + """ Switch the user to the given one. """ + self.client.switch_user(username, password) + + def create_database(self, dbname): + """ Creates a new Influx database. """ + self.client.create_database(dbname) + + def switch_database(self, dbname): + """ Switch the user to the given one. """ + return self.client.switch_database(dbname) + + def write_data(self, data): + """ Writes data to Influx database. """ + self.client.write_points(data) From 38b564e4137784bd3941ac7ccc2ccb189532dc0c Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 25 Nov 2015 22:47:00 +0100 Subject: [PATCH 2/5] Remove class, just use state_changed, and update the export data --- homeassistant/components/influx.py | 123 ++++++++++++----------------- 1 file changed, 50 insertions(+), 73 deletions(-) diff --git a/homeassistant/components/influx.py b/homeassistant/components/influx.py index ef28308d77e..78c3d15787e 100644 --- a/homeassistant/components/influx.py +++ b/homeassistant/components/influx.py @@ -16,20 +16,19 @@ influx: dbuser_password: DB_USER_PASSWORD """ import logging -import requests -import socket import homeassistant.util as util from homeassistant.helpers import validate_config -from homeassistant.const import (MATCH_ALL) +from homeassistant.const import (EVENT_STATE_CHANGED, STATE_ON, STATE_OFF, + STATE_UNLOCKED, STATE_LOCKED, STATE_UNKNOWN) +from homeassistant.components.sun import (STATE_ABOVE_HORIZON, + STATE_BELOW_HORIZON) _LOGGER = logging.getLogger(__name__) DOMAIN = "influx" DEPENDENCIES = ['recorder'] -INFLUX_CLIENT = None - DEFAULT_HOST = 'localhost' DEFAULT_PORT = 8086 DEFAULT_DATABASE = 'home_assistant' @@ -46,7 +45,7 @@ CONF_PASSWORD = 'password' def setup(hass, config): """ Setup the Influx component. """ - from influxdb import exceptions + from influxdb import InfluxDBClient, exceptions if not validate_config(config, {DOMAIN: ['host']}, _LOGGER): return False @@ -59,85 +58,63 @@ def setup(hass, config): username = util.convert(conf.get(CONF_USERNAME), str) password = util.convert(conf.get(CONF_PASSWORD), str) - global INFLUX_CLIENT - try: - INFLUX_CLIENT = Influx(host, port, username, password, dbname) - except (socket.gaierror, requests.exceptions.ConnectionError): - _LOGGER.error("Database is not accessible. " + influx = InfluxDBClient(host=host, port=port, username=username, + password=password, database=dbname) + databases = [i['name'] for i in influx.get_list_database()] + except exceptions.InfluxDBClientError: + _LOGGER.error("Database host is not accessible. " "Please check your entries in the configuration file.") return False - try: - INFLUX_CLIENT.create_database(dbname) - except exceptions.InfluxDBClientError: - _LOGGER.info("Database '%s' already exists", dbname) - - INFLUX_CLIENT.switch_user(username, password) - INFLUX_CLIENT.switch_database(dbname) + if dbname not in databases: + _LOGGER.error("Database %s doesn't exist", dbname) + return False def event_listener(event): """ Listen for new messages on the bus and sends them to Influx. """ event_data = event.as_dict() - json_body = [] - if event_data['event_type'] is not 'time_changed': - try: - entity_id = event_data['data']['entity_id'] - new_state = event_data['data']['new_state'] + if event_data['event_type'] is not EVENT_STATE_CHANGED: + return - json_body = [ - { - "measurement": entity_id.split('.')[1], - "tags": { - "type": entity_id.split('.')[0], - }, - "time": event_data['time_fired'], - "fields": { - "value": new_state.state - } - } - ] - except KeyError: - pass + state = event_data['data']['new_state'] + + if state.state == STATE_ON or state.state == STATE_LOCKED or \ + state.state == STATE_ABOVE_HORIZON: + _state = 1 + elif state.state == STATE_OFF or state.state == STATE_UNLOCKED or \ + state.state == STATE_UNKNOWN or \ + state.state == STATE_BELOW_HORIZON: + _state = 0 + else: + _state = state.state + + try: + measurement = state.attributes['unit_of_measurement'] + except KeyError: + measurement = '{}'.format(state.domain) + + json_body = [ + { + 'measurement': measurement, + 'tags': { + 'domain': state.domain, + 'entity_id': state.object_id, + }, + 'time': event_data['time_fired'], + 'fields': { + 'value': _state, + } + } + ] if json_body: - INFLUX_CLIENT.write_data(json_body) + try: + influx.write_points(json_body) + except exceptions.InfluxDBClientError: + _LOGGER.error("Field type conflict") - hass.bus.listen(MATCH_ALL, event_listener) + hass.bus.listen(EVENT_STATE_CHANGED, event_listener) return True - - -# pylint: disable=too-many-arguments -class Influx(object): - """ Implements the handling of an connection to an Influx database.. """ - - def __init__(self, host, port, username, password, dbname): - - from influxdb import InfluxDBClient - - self._host = host - self._port = port - self._username = username - self._password = password - self._dbname = dbname - - self.client = InfluxDBClient(self._host, self._port, self._username, - self._password, self._dbname) - - def switch_user(self, username, password): - """ Switch the user to the given one. """ - self.client.switch_user(username, password) - - def create_database(self, dbname): - """ Creates a new Influx database. """ - self.client.create_database(dbname) - - def switch_database(self, dbname): - """ Switch the user to the given one. """ - return self.client.switch_database(dbname) - - def write_data(self, data): - """ Writes data to Influx database. """ - self.client.write_points(data) From 08da1e6f8d8c07b1d79131103916560d14ed35ac Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 26 Nov 2015 23:57:34 +0100 Subject: [PATCH 3/5] Add influx component --- .coveragerc | 1 + requirements_all.txt | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 8efc162b3eb..e41d31b2830 100644 --- a/.coveragerc +++ b/.coveragerc @@ -49,6 +49,7 @@ omit = homeassistant/components/discovery.py homeassistant/components/downloader.py homeassistant/components/ifttt.py + homeassistant/components/influx.py homeassistant/components/keyboard.py homeassistant/components/light/hue.py homeassistant/components/light/mqtt.py diff --git a/requirements_all.txt b/requirements_all.txt index a9f24cdb65f..e8281efcfd6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -23,6 +23,9 @@ netdisco==0.5.1 # homeassistant.components.ifttt pyfttt==0.3 +# homeassistant.components.influx +influxdb==2.10.0 + # homeassistant.components.isy994 PyISY==1.0.5 @@ -165,4 +168,3 @@ https://github.com/persandstrom/python-verisure/archive/9873c4527f01b1ba1f72ae60 # homeassistant.components.zwave pydispatcher==2.0.5 - From 74b37bd61b2db9a4be25b9d7a9d10455f3697821 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 26 Nov 2015 23:57:57 +0100 Subject: [PATCH 4/5] Change to exception instead of error --- homeassistant/components/influx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/influx.py b/homeassistant/components/influx.py index 78c3d15787e..5de2f3bdd73 100644 --- a/homeassistant/components/influx.py +++ b/homeassistant/components/influx.py @@ -113,7 +113,7 @@ def setup(hass, config): try: influx.write_points(json_body) except exceptions.InfluxDBClientError: - _LOGGER.error("Field type conflict") + _LOGGER.exception('Error saving event to Influx') hass.bus.listen(EVENT_STATE_CHANGED, event_listener) From 0383dddae7ad28dd51f26408f017a28729eff69e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 4 Dec 2015 16:25:34 +0100 Subject: [PATCH 5/5] Remove dependency --- homeassistant/components/influx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/influx.py b/homeassistant/components/influx.py index 5de2f3bdd73..e8b458892cc 100644 --- a/homeassistant/components/influx.py +++ b/homeassistant/components/influx.py @@ -27,7 +27,7 @@ from homeassistant.components.sun import (STATE_ABOVE_HORIZON, _LOGGER = logging.getLogger(__name__) DOMAIN = "influx" -DEPENDENCIES = ['recorder'] +DEPENDENCIES = [] DEFAULT_HOST = 'localhost' DEFAULT_PORT = 8086