Merge dev into sensor_template
commit
ab9ab532c8
|
@ -126,7 +126,9 @@ def _handle_get_api_stream(handler, path_match, data):
|
||||||
wfile.write(msg.encode("UTF-8"))
|
wfile.write(msg.encode("UTF-8"))
|
||||||
wfile.flush()
|
wfile.flush()
|
||||||
handler.server.sessions.extend_validation(session_id)
|
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()
|
block.set()
|
||||||
|
|
||||||
def forward_events(event):
|
def forward_events(event):
|
||||||
|
|
|
@ -21,14 +21,14 @@ from homeassistant.const import (
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
MediaPlayerDevice,
|
MediaPlayerDevice,
|
||||||
SUPPORT_PAUSE, SUPPORT_VOLUME_SET, SUPPORT_TURN_OFF,
|
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)
|
MEDIA_TYPE_MUSIC)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
REQUIREMENTS = ['python-mpd2==0.5.4']
|
REQUIREMENTS = ['python-mpd2==0.5.4']
|
||||||
|
|
||||||
SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \
|
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
|
# pylint: disable=unused-argument
|
||||||
|
@ -163,9 +163,13 @@ class MpdDevice(MediaPlayerDevice):
|
||||||
return SUPPORT_MPD
|
return SUPPORT_MPD
|
||||||
|
|
||||||
def turn_off(self):
|
def turn_off(self):
|
||||||
""" Service to exit the running MPD. """
|
""" Service to send the MPD the command to stop playing. """
|
||||||
self.client.stop()
|
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):
|
def set_volume_level(self, volume):
|
||||||
""" Sets volume """
|
""" Sets volume """
|
||||||
self.client.setvol(int(volume * 100))
|
self.client.setvol(int(volume * 100))
|
||||||
|
|
|
@ -14,3 +14,10 @@ class InvalidEntityFormatError(HomeAssistantError):
|
||||||
class NoEntitySpecifiedError(HomeAssistantError):
|
class NoEntitySpecifiedError(HomeAssistantError):
|
||||||
""" When no entity is specified. """
|
""" When no entity is specified. """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateError(HomeAssistantError):
|
||||||
|
""" Error during template rendering. """
|
||||||
|
def __init__(self, exception):
|
||||||
|
super().__init__('{}: {}'.format(exception.__class__.__name__,
|
||||||
|
exception))
|
||||||
|
|
|
@ -6,10 +6,17 @@ Template utility methods for rendering strings with HA data.
|
||||||
"""
|
"""
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import jinja2
|
||||||
from jinja2.sandbox import ImmutableSandboxedEnvironment
|
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.
|
""" Renders template with value exposed.
|
||||||
If valid JSON will expose value_json too. """
|
If valid JSON will expose value_json too. """
|
||||||
variables = {
|
variables = {
|
||||||
|
@ -20,7 +27,11 @@ def render_with_possible_json_value(hass, template, value):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
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):
|
def render(hass, template, variables=None, **kwargs):
|
||||||
|
@ -28,9 +39,12 @@ def render(hass, template, variables=None, **kwargs):
|
||||||
if variables is not None:
|
if variables is not None:
|
||||||
kwargs.update(variables)
|
kwargs.update(variables)
|
||||||
|
|
||||||
return ENV.from_string(template, {
|
try:
|
||||||
'states': AllStates(hass)
|
return ENV.from_string(template, {
|
||||||
}).render(kwargs)
|
'states': AllStates(hass)
|
||||||
|
}).render(kwargs)
|
||||||
|
except jinja2.TemplateError as err:
|
||||||
|
raise TemplateError(err)
|
||||||
|
|
||||||
|
|
||||||
class AllStates(object):
|
class AllStates(object):
|
||||||
|
@ -66,8 +80,8 @@ class DomainStates(object):
|
||||||
def forgiving_round(value, precision=0):
|
def forgiving_round(value, precision=0):
|
||||||
""" Rounding method that accepts strings. """
|
""" Rounding method that accepts strings. """
|
||||||
try:
|
try:
|
||||||
return int(float(value)) if precision == 0 else round(float(value),
|
value = round(float(value), precision)
|
||||||
precision)
|
return int(value) if precision == 0 else value
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# If value can't be converted to float
|
# If value can't be converted to float
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -7,7 +7,7 @@ Tests Home Assistant util methods.
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
import unittest
|
import unittest
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
|
from homeassistant.exceptions import TemplateError
|
||||||
from homeassistant.util import template
|
from homeassistant.util import template
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,10 +57,10 @@ class TestUtilTemplate(unittest.TestCase):
|
||||||
'{{ states.sensor.temperature.state | round(1) }}'))
|
'{{ states.sensor.temperature.state | round(1) }}'))
|
||||||
|
|
||||||
def test_rounding_value2(self):
|
def test_rounding_value2(self):
|
||||||
self.hass.states.set('sensor.temperature', 12.72)
|
self.hass.states.set('sensor.temperature', 12.78)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'127',
|
'128',
|
||||||
template.render(
|
template.render(
|
||||||
self.hass,
|
self.hass,
|
||||||
'{{ states.sensor.temperature.state | multiply(10) | round }}'))
|
'{{ states.sensor.temperature.state | multiply(10) | round }}'))
|
||||||
|
@ -84,3 +84,19 @@ class TestUtilTemplate(unittest.TestCase):
|
||||||
'',
|
'',
|
||||||
template.render_with_possible_json_value(
|
template.render_with_possible_json_value(
|
||||||
self.hass, '{{ value_json }}', '{ I AM NOT JSON }'))
|
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')
|
||||||
|
|
Loading…
Reference in New Issue