def create_service(call): """Handle a create notification service call.""" title = call.data.get(ATTR_TITLE) message = call.data.get(ATTR_MESSAGE) notification_id = call.data.get(ATTR_NOTIFICATION_ID) if notification_id is not None: entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id)) else: entity_id = generate_entity_id(ENTITY_ID_FORMAT, DEFAULT_OBJECT_ID, hass=hass) attr = {} if title is not None: try: title = template.render(hass, title) except TemplateError as ex: _LOGGER.error('Error rendering title %s: %s', title, ex) attr[ATTR_TITLE] = title try: message = template.render(hass, message) except TemplateError as ex: _LOGGER.error('Error rendering message %s: %s', message, ex) hass.states.set(entity_id, message, attr)
def test_float(self): """.""" self.hass.states.set("sensor.temperature", "12") self.assertEqual("12.0", template.render(self.hass, "{{ float(states.sensor.temperature.state) }}")) self.assertEqual("True", template.render(self.hass, "{{ float(states.sensor.temperature.state) > 11 }}"))
def test_rounding_value_get_original_value_on_error(self): """.""" self.assertEqual('None', template.render(self.hass, '{{ None | round }}')) self.assertEqual( 'no_number', template.render(self.hass, '{{ "no_number" | round }}'))
def test_states_function(self): self.hass.states.set('test.object', 'available') self.assertEqual( 'available', template.render(self.hass, '{{ states("test.object") }}')) self.assertEqual( 'unknown', template.render(self.hass, '{{ states("test.object2") }}'))
def test_rounding_value(self): """.""" self.hass.states.set("sensor.temperature", 12.78) self.assertEqual("12.8", template.render(self.hass, "{{ states.sensor.temperature.state | round(1) }}")) self.assertEqual( "128", template.render(self.hass, "{{ states.sensor.temperature.state | multiply(10) | round }}") )
def test_distance_function_return_None_if_invalid_coord(self): """.""" self.assertEqual("None", template.render(self.hass, '{{ distance("123", "abc") }}')) self.assertEqual("None", template.render(self.hass, '{{ distance("123") }}')) self.hass.states.set( "test.object_2", "happy", {"latitude": self.hass.config.latitude, "longitude": self.hass.config.longitude} ) self.assertEqual("None", template.render(self.hass, '{{ distance("123", states.test_object_2) }}'))
def notify_message(notify_service, call): """Handle sending notification message service calls.""" message = call.data[ATTR_MESSAGE] title = template.render( hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)) target = call.data.get(ATTR_TARGET) message = template.render(hass, message) data = call.data.get(ATTR_DATA) notify_service.send_message(message, title=title, target=target, data=data)
def notify_message(notify_service, call): """Handle sending notification message service calls.""" message = call.data.get(ATTR_MESSAGE) if message is None: return title = template.render( hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)) target = call.data.get(ATTR_TARGET) message = template.render(hass, message) notify_service.send_message(message, title=title, target=target)
def test_rounding_value_get_original_value_on_error(self): self.assertEqual( 'None', template.render( self.hass, '{{ None | round }}' )) self.assertEqual( 'no_number', template.render( self.hass, '{{ "no_number" | round }}' ))
def test_float(self): self.hass.states.set('sensor.temperature', '12') self.assertEqual( '12.0', template.render( self.hass, '{{ float(states.sensor.temperature.state) }}')) self.assertEqual( 'True', template.render( self.hass, '{{ float(states.sensor.temperature.state) > 11 }}'))
def test_float(self): """.""" self.hass.states.set('sensor.temperature', '12') self.assertEqual( '12.0', template.render(self.hass, '{{ float(states.sensor.temperature.state) }}')) self.assertEqual( 'True', template.render( self.hass, '{{ float(states.sensor.temperature.state) > 11 }}'))
def test_rounding_value(self): self.hass.states.set('sensor.temperature', 12.78) self.assertEqual( '12.8', template.render( self.hass, '{{ states.sensor.temperature.state | round(1) }}')) self.assertEqual( '128', template.render( self.hass, '{{ states.sensor.temperature.state | multiply(10) | round }}' ))
def test_rounding_value(self): """.""" self.hass.states.set('sensor.temperature', 12.78) self.assertEqual( '12.8', template.render( self.hass, '{{ states.sensor.temperature.state | round(1) }}')) self.assertEqual( '128', template.render( self.hass, '{{ states.sensor.temperature.state | multiply(10) | round }}') )
def notify_message(notify_service, call): """Handle sending notification message service calls.""" message = call.data.get(ATTR_MESSAGE) if message is None: _LOGGER.error('Received call to %s without attribute %s', call.service, ATTR_MESSAGE) return title = template.render( hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)) target = call.data.get(ATTR_TARGET) message = template.render(hass, message) notify_service.send_message(message, title=title, target=target)
def test_distance_function_with_1_state_1_coord(self): """.""" self.hass.states.set( "test.object_2", "happy", {"latitude": self.hass.config.latitude, "longitude": self.hass.config.longitude} ) self.assertEqual( "187", template.render(self.hass, '{{ distance("32.87336", "-117.22943", states.test.object_2) ' "| round }}"), ) self.assertEqual( "187", template.render(self.hass, '{{ distance(states.test.object_2, "32.87336", "-117.22943") ' "| round }}"), )
def notify_message(notify_service, call): """Handle sending notification message service calls.""" message = call.data.get(ATTR_MESSAGE) if message is None: _LOGGER.error( 'Received call to %s without attribute %s', call.service, ATTR_MESSAGE) return title = template.render( hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)) target = call.data.get(ATTR_TARGET) message = template.render(hass, message) notify_service.send_message(message, title=title, target=target)
def numeric_state(hass, entity, below=None, above=None, value_template=None, variables=None): """Test a numeric state condition.""" if isinstance(entity, str): entity = hass.states.get(entity) if entity is None: return False if value_template is None: value = entity.state else: variables = dict(variables or {}) variables['state'] = entity try: value = render(hass, value_template, variables) except TemplateError as ex: _LOGGER.error(ex) return False try: value = float(value) except ValueError: _LOGGER.warning("Value cannot be processed as a number: %s", value) return False if below is not None and value > below: return False if above is not None and value < above: return False return True
def test_closest_function_to_coord(self): """.""" self.hass.states.set( "test_domain.closest_home", "happy", {"latitude": self.hass.config.latitude + 0.1, "longitude": self.hass.config.longitude + 0.1}, ) self.hass.states.set( "test_domain.closest_zone", "happy", {"latitude": self.hass.config.latitude + 0.2, "longitude": self.hass.config.longitude + 0.2}, ) self.hass.states.set( "zone.far_away", "zoning", {"latitude": self.hass.config.latitude + 0.3, "longitude": self.hass.config.longitude + 0.3}, ) self.assertEqual( "test_domain.closest_zone", template.render( self.hass, '{{ closest("%s", %s, states.test_domain).entity_id }}' % (self.hass.config.latitude + 0.3, self.hass.config.longitude + 0.3), ), )
def test_closest_function_to_state(self): """.""" self.hass.states.set( 'test_domain.closest_home', 'happy', { 'latitude': self.hass.config.latitude + 0.1, 'longitude': self.hass.config.longitude + 0.1, }) self.hass.states.set( 'test_domain.closest_zone', 'happy', { 'latitude': self.hass.config.latitude + 0.2, 'longitude': self.hass.config.longitude + 0.2, }) self.hass.states.set( 'zone.far_away', 'zoning', { 'latitude': self.hass.config.latitude + 0.3, 'longitude': self.hass.config.longitude + 0.3, }) self.assertEqual( 'test_domain.closest_zone', template.render( self.hass, '{{ closest(states.zone.far_away, ' 'states.test_domain).entity_id }}'))
def numeric_state(hass: HomeAssistant, entity, below=None, above=None, value_template=None, variables=None): """Test a numeric state condition.""" if isinstance(entity, str): entity = hass.states.get(entity) if entity is None: return False if value_template is None: value = entity.state else: variables = dict(variables or {}) variables['state'] = entity try: value = render(hass, value_template, variables) except TemplateError as ex: _LOGGER.error("Template error: %s", ex) return False try: value = float(value) except ValueError: _LOGGER.warning("Value cannot be processed as a number: %s", value) return False if below is not None and value > below: return False if above is not None and value < above: return False return True
def test_closest_function_to_coord(self): """.""" self.hass.states.set('test_domain.closest_home', 'happy', { 'latitude': self.hass.config.latitude + 0.1, 'longitude': self.hass.config.longitude + 0.1, }) self.hass.states.set('test_domain.closest_zone', 'happy', { 'latitude': self.hass.config.latitude + 0.2, 'longitude': self.hass.config.longitude + 0.2, }) self.hass.states.set('zone.far_away', 'zoning', { 'latitude': self.hass.config.latitude + 0.3, 'longitude': self.hass.config.longitude + 0.3, }) self.assertEqual( 'test_domain.closest_zone', template.render( self.hass, '{{ closest("%s", %s, states.test_domain).entity_id }}' % (self.hass.config.latitude + 0.3, self.hass.config.longitude + 0.3)) )
def test_distance_function_with_2_coords(self): self.assertEqual( '187', template.render( self.hass, '{{ distance("32.87336", "-117.22943", %s, %s) | round }}' % (self.hass.config.latitude, self.hass.config.longitude)))
def post(self, request): """Render a template.""" try: return template.render(self.hass, request.json['template'], request.json.get('variables')) except TemplateError as ex: return self.json_message('Error rendering template: {}'.format(ex), HTTP_BAD_REQUEST)
def test_iterating_all_states(self): """.""" self.hass.states.set("test.object", "happy") self.hass.states.set("sensor.temperature", 10) self.assertEqual( "10happy", template.render(self.hass, "{% for state in states %}{{ state.state }}{% endfor %}") )
def test_iterating_all_states(self): self.hass.states.set('test.object', 'happy') self.hass.states.set('sensor.temperature', 10) self.assertEqual( '10happy', template.render( self.hass, '{% for state in states %}{{ state.state }}{% endfor %}'))
def test_multiply(self): """.""" tests = {None: 'None', 10: '100', '"abcd"': 'abcd'} for inp, out in tests.items(): self.assertEqual( out, template.render(self.hass, '{{ %s | multiply(10) | round }}' % inp))
def notify_message(notify_service, call): """Handle sending notification message service calls.""" kwargs = {} message = call.data[ATTR_MESSAGE] title = call.data.get(ATTR_TITLE) if title: kwargs[ATTR_TITLE] = template.render(hass, title) if targets.get(call.service) is not None: kwargs[ATTR_TARGET] = targets[call.service] else: kwargs[ATTR_TARGET] = call.data.get(ATTR_TARGET) kwargs[ATTR_MESSAGE] = template.render(hass, message) kwargs[ATTR_DATA] = call.data.get(ATTR_DATA) notify_service.send_message(**kwargs)
def test_distance_function_return_None_if_invalid_coord(self): """.""" self.assertEqual( 'None', template.render(self.hass, '{{ distance("123", "abc") }}')) self.assertEqual('None', template.render(self.hass, '{{ distance("123") }}')) self.hass.states.set( 'test.object_2', 'happy', { 'latitude': self.hass.config.latitude, 'longitude': self.hass.config.longitude, }) self.assertEqual( 'None', template.render(self.hass, '{{ distance("123", states.test_object_2) }}'))
def test_is_state_attr(self): """.""" self.hass.states.set('test.object', 'available', {'mode': 'on'}) self.assertEqual( 'yes', template.render( self.hass, """ {% if is_state_attr("test.object", "mode", "on") %}yes{% else %}no{% endif %} """))
def log_message(service): """Handle sending notification message service calls.""" message = service.data[ATTR_MESSAGE] name = service.data[ATTR_NAME] domain = service.data.get(ATTR_DOMAIN) entity_id = service.data.get(ATTR_ENTITY_ID) message = template.render(hass, message) log_entry(hass, name, message, domain, entity_id)
def test_is_state(self): """.""" self.hass.states.set('test.object', 'available') self.assertEqual( 'yes', template.render( self.hass, """ {% if is_state("test.object", "available") %}yes{% else %}no{% endif %} """))
def template(hass, value_template, variables=None): """Test if template condition matches.""" try: value = render(hass, value_template, variables) except TemplateError as ex: _LOGGER.error('Error duriong template condition: %s', ex) return False return value.lower() == 'true'
def test_if_state_exists(self): """.""" self.hass.states.set('test.object', 'available') self.assertEqual( 'exists', template.render( self.hass, """ {% if states.test.object %}exists{% else %}not exists{% endif %} """))
def test_if_state_exists(self): self.hass.states.set('test.object', 'available') self.assertEqual( 'exists', template.render( self.hass, """ {% if states.test.object %}exists{% else %}not exists{% endif %} """))
def _check_template(hass, value_template): """ Checks if result of template is true """ try: value = template.render(hass, value_template, {}) except TemplateError: _LOGGER.exception('Error parsing template') return False return value.lower() == 'true'
def test_is_state(self): self.hass.states.set('test.object', 'available') self.assertEqual( 'yes', template.render( self.hass, """ {% if is_state("test.object", "available") %}yes{% else %}no{% endif %} """))
def test_is_state_attr(self): self.hass.states.set('test.object', 'available', {'mode': 'on'}) self.assertEqual( 'yes', template.render( self.hass, """ {% if is_state_attr("test.object", "mode", "on") %}yes{% else %}no{% endif %} """))
def test_closest_function_invalid_coordinates(self): """.""" self.hass.states.set( "test_domain.closest_home", "happy", {"latitude": self.hass.config.latitude + 0.1, "longitude": self.hass.config.longitude + 0.1}, ) self.assertEqual("None", template.render(self.hass, '{{ closest("invalid", "coord", states) }}'))
def test_distance_function_return_None_if_invalid_state(self): """.""" self.hass.states.set('test.object_2', 'happy', { 'latitude': 10, }) self.assertEqual( 'None', template.render(self.hass, '{{ distance(states.test.object_2) | round }}'))
def update(self): """Updates the state from the template.""" try: self._value = template.render(self.hass, self._template) if not self.available: _LOGGER.error("`%s` is not a switch state, setting %s to unavailable", self._value, self.entity_id) except TemplateError as ex: self._value = STATE_ERROR _LOGGER.error(ex)
def test_closest_function_invalid_coordinates(self): self.hass.states.set('test_domain.closest_home', 'happy', { 'latitude': self.hass.config.latitude + 0.1, 'longitude': self.hass.config.longitude + 0.1, }) self.assertEqual( 'None', template.render(self.hass, '{{ closest("invalid", "coord", states) }}'))
def test_distance_function_with_1_state(self): self.hass.states.set('test.object', 'happy', { 'latitude': 32.87336, 'longitude': -117.22943, }) self.assertEqual( '187', template.render( self.hass, '{{ distance(states.test.object) | round }}'))
def test_closest_function_invalid_state(self): """.""" self.hass.states.set( "test_domain.closest_home", "happy", {"latitude": self.hass.config.latitude + 0.1, "longitude": self.hass.config.longitude + 0.1}, ) for state in ("states.zone.non_existing", '"zone.non_existing"'): self.assertEqual("None", template.render(self.hass, "{{ closest(%s, states) }}" % state))
def test_distance_function_with_1_state_1_coord(self): self.hass.states.set('test.object_2', 'happy', { 'latitude': self.hass.config.latitude, 'longitude': self.hass.config.longitude, }) self.assertEqual( '187', template.render( self.hass, '{{ distance("32.87336", "-117.22943", states.test.object_2) ' '| round }}')) self.assertEqual( '187', template.render( self.hass, '{{ distance(states.test.object_2, "32.87336", "-117.22943") ' '| round }}'))
def call_from_config(hass, config, blocking=False): """Call a service based on a config hash.""" validation_error = validate_service_call(config) if validation_error: _LOGGER.error(validation_error) return domain_service = (config[CONF_SERVICE] if CONF_SERVICE in config else template.render(hass, config[CONF_SERVICE_TEMPLATE])) try: domain, service_name = domain_service.split('.', 1) except ValueError: _LOGGER.error('Invalid service specified: %s', domain_service) return service_data = config.get(CONF_SERVICE_DATA) if service_data is None: service_data = {} elif isinstance(service_data, dict): service_data = dict(service_data) else: _LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA) service_data = {} service_data_template = config.get(CONF_SERVICE_DATA_TEMPLATE) if service_data_template and isinstance(service_data_template, dict): for key, value in service_data_template.items(): service_data[key] = template.render(hass, value) elif service_data_template: _LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA) entity_id = config.get(CONF_SERVICE_ENTITY_ID) if isinstance(entity_id, str): service_data[ATTR_ENTITY_ID] = [ ent.strip() for ent in entity_id.split(",") ] elif entity_id is not None: service_data[ATTR_ENTITY_ID] = entity_id hass.services.call(domain, service_name, service_data, blocking)
def test_closest_function_invalid_state(self): self.hass.states.set('test_domain.closest_home', 'happy', { 'latitude': self.hass.config.latitude + 0.1, 'longitude': self.hass.config.longitude + 0.1, }) for state in ('states.zone.non_existing', '"zone.non_existing"'): self.assertEqual( 'None', template.render( self.hass, '{{ closest(%s, states) }}' % state))
def test_timestamp_local(self): """Test the timestamps to local filter.""" tests = { None: 'None', 1469119144: '2016-07-21 16:39:04', } for inp, out in tests.items(): self.assertEqual( out, template.render(self.hass, '{{ %s | timestamp_local }}' % inp))
def _data_template_creator(value): """Recursive template creator helper function.""" if isinstance(value, list): for idx, element in enumerate(value): value[idx] = _data_template_creator(element) return value if isinstance(value, dict): for key, element in value.items(): value[key] = _data_template_creator(element) return value return template.render(hass, value, variables)