diff --git a/homeassistant/components/graphite.py b/homeassistant/components/graphite.py index c262443a94d..4fef17f0927 100644 --- a/homeassistant/components/graphite.py +++ b/homeassistant/components/graphite.py @@ -1,5 +1,5 @@ """ -Component that sends data to aGraphite installation. +Component that sends data to a Graphite installation. For more details about this component, please refer to the documentation at https://home-assistant.io/components/graphite/ @@ -10,26 +10,48 @@ import socket import threading import time -from homeassistant.const import ( - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_STATE_CHANGED) -from homeassistant.helpers import state +import voluptuous as vol + +from homeassistant.const import ( + CONF_HOST, CONF_PORT, CONF_PREFIX, EVENT_HOMEASSISTANT_START, + EVENT_HOMEASSISTANT_STOP, EVENT_STATE_CHANGED) +from homeassistant.helpers import state +import homeassistant.helpers.config_validation as cv -DOMAIN = "graphite" _LOGGER = logging.getLogger(__name__) +DEFAULT_HOST = 'localhost' +DEFAULT_PORT = 2003 +DEFAULT_PREFIX = 'ha' +DOMAIN = 'graphite' + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_PREFIX, default=DEFAULT_PREFIX): cv.string, + }), +}, extra=vol.ALLOW_EXTRA) + def setup(hass, config): """Setup the Graphite feeder.""" - graphite_config = config.get('graphite', {}) - host = graphite_config.get('host', 'localhost') - prefix = graphite_config.get('prefix', 'ha') + conf = config[DOMAIN] + host = conf.get(CONF_HOST) + prefix = conf.get(CONF_PREFIX) + port = conf.get(CONF_PORT) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: - port = int(graphite_config.get('port', 2003)) - except ValueError: - _LOGGER.error('Invalid port specified') + sock.connect((host, port)) + sock.shutdown(2) + _LOGGER.debug('Connection to Graphite possible') + except socket.error: + _LOGGER.error('Not able to connect to Graphite') return False GraphiteFeeder(hass, host, port, prefix) + return True diff --git a/tests/components/test_graphite.py b/tests/components/test_graphite.py index 9e9ea837dfe..bb60d81a155 100644 --- a/tests/components/test_graphite.py +++ b/tests/components/test_graphite.py @@ -2,15 +2,15 @@ import socket import unittest from unittest import mock +from unittest.mock import patch import homeassistant.core as ha import homeassistant.components.graphite as graphite from homeassistant.const import ( - EVENT_STATE_CHANGED, - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, + EVENT_STATE_CHANGED, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, STATE_ON, STATE_OFF) - from tests.common import get_test_home_assistant +from homeassistant import bootstrap class TestGraphite(unittest.TestCase): @@ -19,22 +19,22 @@ class TestGraphite(unittest.TestCase): def setup_method(self, method): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() - self.hass.config.latitude = 32.87336 - self.hass.config.longitude = 117.22743 self.gf = graphite.GraphiteFeeder(self.hass, 'foo', 123, 'ha') def teardown_method(self, method): """Stop everything that was started.""" self.hass.stop() - @mock.patch('homeassistant.components.graphite.GraphiteFeeder') - def test_minimal_config(self, mock_gf): - """Test setup with minimal configuration.""" - self.assertTrue(graphite.setup(self.hass, {})) - mock_gf.assert_called_once_with(self.hass, 'localhost', 2003, 'ha') + @patch('socket.socket') + def test_setup(self, mock_socket): + """Test setup.""" + assert bootstrap.setup_component(self.hass, 'graphite', + {'graphite': {}}) + mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM) - @mock.patch('homeassistant.components.graphite.GraphiteFeeder') - def test_full_config(self, mock_gf): + @patch('socket.socket') + @patch('homeassistant.components.graphite.GraphiteFeeder') + def test_full_config(self, mock_gf, mock_socket): """Test setup with full configuration.""" config = { 'graphite': { @@ -43,20 +43,25 @@ class TestGraphite(unittest.TestCase): 'prefix': 'me', } } + self.assertTrue(graphite.setup(self.hass, config)) mock_gf.assert_called_once_with(self.hass, 'foo', 123, 'me') + mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM) - @mock.patch('homeassistant.components.graphite.GraphiteFeeder') - def test_config_bad_port(self, mock_gf): + @patch('socket.socket') + @patch('homeassistant.components.graphite.GraphiteFeeder') + def test_config_port(self, mock_gf, mock_socket): """Test setup with invalid port.""" config = { 'graphite': { 'host': 'foo', - 'port': 'wrong', + 'port': 2003, } } - self.assertFalse(graphite.setup(self.hass, config)) - self.assertFalse(mock_gf.called) + + self.assertTrue(graphite.setup(self.hass, config)) + self.assertTrue(mock_gf.called) + mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM) def test_subscribe(self): """Test the subscription.""" @@ -87,7 +92,7 @@ class TestGraphite(unittest.TestCase): self.gf.event_listener('foo') mock_queue.put.assert_called_once_with('foo') - @mock.patch('time.time') + @patch('time.time') def test_report_attributes(self, mock_time): """Test the reporting with attributes.""" mock_time.return_value = 12345 @@ -96,19 +101,21 @@ class TestGraphite(unittest.TestCase): 'baz': True, 'bat': 'NaN', } + expected = [ 'ha.entity.state 0.000000 12345', 'ha.entity.foo 1.000000 12345', 'ha.entity.bar 2.000000 12345', 'ha.entity.baz 1.000000 12345', ] + state = mock.MagicMock(state=0, attributes=attrs) with mock.patch.object(self.gf, '_send_to_graphite') as mock_send: self.gf._report_attributes('entity', state) actual = mock_send.call_args_list[0][0][0].split('\n') self.assertEqual(sorted(expected), sorted(actual)) - @mock.patch('time.time') + @patch('time.time') def test_report_with_string_state(self, mock_time): """Test the reporting with strings.""" mock_time.return_value = 12345 @@ -116,13 +123,14 @@ class TestGraphite(unittest.TestCase): 'ha.entity.foo 1.000000 12345', 'ha.entity.state 1.000000 12345', ] + state = mock.MagicMock(state='above_horizon', attributes={'foo': 1.0}) with mock.patch.object(self.gf, '_send_to_graphite') as mock_send: self.gf._report_attributes('entity', state) actual = mock_send.call_args_list[0][0][0].split('\n') self.assertEqual(sorted(expected), sorted(actual)) - @mock.patch('time.time') + @patch('time.time') def test_report_with_binary_state(self, mock_time): """Test the reporting with binary state.""" mock_time.return_value = 12345 @@ -142,7 +150,7 @@ class TestGraphite(unittest.TestCase): actual = mock_send.call_args_list[0][0][0].split('\n') self.assertEqual(sorted(expected), sorted(actual)) - @mock.patch('time.time') + @patch('time.time') def test_send_to_graphite_errors(self, mock_time): """Test the sending with errors.""" mock_time.return_value = 12345 @@ -153,7 +161,7 @@ class TestGraphite(unittest.TestCase): mock_send.side_effect = socket.gaierror self.gf._report_attributes('entity', state) - @mock.patch('socket.socket') + @patch('socket.socket') def test_send_to_graphite(self, mock_socket): """Test the sending of data.""" self.gf._send_to_graphite('foo')