diff --git a/homeassistant/components/api.py b/homeassistant/components/api.py index 6a66a2a110e..6d2f9e52a7a 100644 --- a/homeassistant/components/api.py +++ b/homeassistant/components/api.py @@ -126,7 +126,9 @@ def _handle_get_api_stream(handler, path_match, data): wfile.write(msg.encode("UTF-8")) wfile.flush() handler.server.sessions.extend_validation(session_id) - except IOError: + except (IOError, ValueError): + # IOError: socket errors + # ValueError: raised when 'I/O operation on closed file' block.set() def forward_events(event): diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/media_player/mpd.py index 8134ce5e743..b6c89cbfc3f 100644 --- a/homeassistant/components/media_player/mpd.py +++ b/homeassistant/components/media_player/mpd.py @@ -21,14 +21,14 @@ from homeassistant.const import ( from homeassistant.components.media_player import ( MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_VOLUME_SET, SUPPORT_TURN_OFF, - SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, + SUPPORT_TURN_ON, SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, MEDIA_TYPE_MUSIC) _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['python-mpd2==0.5.4'] SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \ - SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK + SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK # pylint: disable=unused-argument @@ -163,9 +163,13 @@ class MpdDevice(MediaPlayerDevice): return SUPPORT_MPD def turn_off(self): - """ Service to exit the running MPD. """ + """ Service to send the MPD the command to stop playing. """ self.client.stop() + def turn_on(self): + """ Service to send the MPD the command to start playing. """ + self.client.play() + def set_volume_level(self, volume): """ Sets volume """ self.client.setvol(int(volume * 100)) diff --git a/homeassistant/exceptions.py b/homeassistant/exceptions.py index bd32d356670..510bc9b4e54 100644 --- a/homeassistant/exceptions.py +++ b/homeassistant/exceptions.py @@ -14,3 +14,10 @@ class InvalidEntityFormatError(HomeAssistantError): class NoEntitySpecifiedError(HomeAssistantError): """ When no entity is specified. """ pass + + +class TemplateError(HomeAssistantError): + """ Error during template rendering. """ + def __init__(self, exception): + super().__init__('{}: {}'.format(exception.__class__.__name__, + exception)) diff --git a/homeassistant/util/template.py b/homeassistant/util/template.py index 107532db776..ad0fabdab53 100644 --- a/homeassistant/util/template.py +++ b/homeassistant/util/template.py @@ -6,10 +6,17 @@ Template utility methods for rendering strings with HA data. """ # pylint: disable=too-few-public-methods import json +import logging +import jinja2 from jinja2.sandbox import ImmutableSandboxedEnvironment +from homeassistant.exceptions import TemplateError + +_LOGGER = logging.getLogger(__name__) +_SENTINEL = object() -def render_with_possible_json_value(hass, template, value): +def render_with_possible_json_value(hass, template, value, + error_value=_SENTINEL): """ Renders template with value exposed. If valid JSON will expose value_json too. """ variables = { @@ -20,7 +27,11 @@ def render_with_possible_json_value(hass, template, value): except ValueError: pass - return render(hass, template, variables) + try: + return render(hass, template, variables) + except TemplateError: + _LOGGER.exception('Error parsing value') + return value if error_value is _SENTINEL else error_value def render(hass, template, variables=None, **kwargs): @@ -28,9 +39,12 @@ def render(hass, template, variables=None, **kwargs): if variables is not None: kwargs.update(variables) - return ENV.from_string(template, { - 'states': AllStates(hass) - }).render(kwargs) + try: + return ENV.from_string(template, { + 'states': AllStates(hass) + }).render(kwargs) + except jinja2.TemplateError as err: + raise TemplateError(err) class AllStates(object): @@ -66,8 +80,8 @@ class DomainStates(object): def forgiving_round(value, precision=0): """ Rounding method that accepts strings. """ try: - return int(float(value)) if precision == 0 else round(float(value), - precision) + value = round(float(value), precision) + return int(value) if precision == 0 else value except ValueError: # If value can't be converted to float return value diff --git a/tests/util/test_template.py b/tests/util/test_template.py index 5c1dfff1f85..ba354f3e7be 100644 --- a/tests/util/test_template.py +++ b/tests/util/test_template.py @@ -7,7 +7,7 @@ Tests Home Assistant util methods. # pylint: disable=too-many-public-methods import unittest import homeassistant.core as ha - +from homeassistant.exceptions import TemplateError from homeassistant.util import template @@ -57,10 +57,10 @@ class TestUtilTemplate(unittest.TestCase): '{{ states.sensor.temperature.state | round(1) }}')) def test_rounding_value2(self): - self.hass.states.set('sensor.temperature', 12.72) + self.hass.states.set('sensor.temperature', 12.78) self.assertEqual( - '127', + '128', template.render( self.hass, '{{ states.sensor.temperature.state | multiply(10) | round }}')) @@ -84,3 +84,19 @@ class TestUtilTemplate(unittest.TestCase): '', template.render_with_possible_json_value( self.hass, '{{ value_json }}', '{ I AM NOT JSON }')) + + def test_render_with_possible_json_value_with_template_error(self): + self.assertEqual( + 'hello', + template.render_with_possible_json_value( + self.hass, '{{ value_json', 'hello')) + + def test_render_with_possible_json_value_with_template_error_error_value(self): + self.assertEqual( + '-', + template.render_with_possible_json_value( + self.hass, '{{ value_json', 'hello', '-')) + + def test_raise_exception_on_error(self): + with self.assertRaises(TemplateError): + template.render(self.hass, '{{ invalid_syntax')